javascript - 在 Promise 中编写同步代码的正确方法是什么?
问题描述
几天前,我在 YouTube 上观看了一段关于James Snell介绍的Broken Promises的有趣视频。
您可以从他的资料库中找到视频中介绍的一些很好的示例。
按照他的说法,我们不应该将纯粹的同步代码包装在 Promise 中。如果我们绝对需要一个函数来返回一个 Promise,那么使用
Promise.resolve() method
. 重要的是,他还指出同步运行您的代码并省去所有这些额外的承诺分配的麻烦。
看完视频后,我查看了我的代码。并且想知道如果我做了与他在剪辑中提到的类似的事情会怎样。
让我给你看一些例子。
这是一个等待 promise 被解决的异步函数。
async getGameShotDetail(buffer: Buffer, fileSize: number): Promise<ShotDetail[]> {
const { latitude, longitude, shotType } = await somefunc()
const data = await Promise.all([
this.parseHoleNumber(shotType),
this.parseShotType(shotType),
this.parseCoordinate(this.sliceBufferIntoPieces(latitude)),
this.parseCoordinate(this.sliceBufferIntoPieces(longitude)),
]);
return some async func(data);
}
前两个方法parseHoleNumber
和方法parseShotType
数组内部的Promise.all()
作用几乎相同。它从二进制文件中读取数据,它们最终都返回一个数字数组作为承诺。
private parseHoleNumber(buffer: number[]): Promise<number[]> {
return new Promise((resolve, reject) => {
if (buffer.length < 0) {
reject([]);
}
/* tslint:disable:no-bitwise */
resolve(buffer.filter(n => n !== 0).map(holeNumber => holeNumber >> 3));
/* tslint:enable:no-bitwise */
});
}
我在这里要完成的是编写返回承诺的同步函数。问题是我不确定我的代码是否像 James Snell 所说的那样写得很好。
据我所知,包括 push 在内的所有Array方法都是同步的。而且我不确定将项目推送到数组中new Promise(executor)
是否安全。
我的代码中是否存在任何易受攻击或错误使用的承诺?
private sliceBufferIntoPieces(
buffer: number[] | string[],
chunkSize: number = 4,
): Promise<Array<number[]>> {
const arr = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < buffer.length; i += chunkSize) {
arr.push(buffer.slice(i, i + chunkSize));
}
arr.length > 0 ? resolve(arr) : reject([]);
});
}
private async parseCoordinate(buffer: Promise<Array<number[]>>): Promise<number[]> {
const itemsAreZero = (item): boolean => item === 0;
return Promise.resolve(
(await buffer) // Maybe this is bad?
.filter(buff => !buff.every(itemsAreZero))
.map(byte => +(this.read4byteItem(byte) / 360000).toFixed(6)),
);
}
我要亲自感谢詹姆斯的精彩演讲
解决方案
您的代码中的所有函数都没有异步执行任何操作。一旦await somefunc
线路运行,您所做的一切都是同步的,但Promise.all
由于某种原因,您仍然将所有内容包装在调用中。Promise.resolve
您可以通过删除不必要的 Promise 结构和s来解决这个问题(并避免视频讨论的反模式) :
async getGameShotDetail(buffer: Buffer, fileSize: number): Promise<ShotDetail[]> {
const { latitude, longitude, shotType } = await somefunc()
const data = [
this.parseHoleNumber(shotType),
this.parseShotType(shotType),
this.parseCoordinate(this.sliceBufferIntoPieces(latitude)),
this.parseCoordinate(this.sliceBufferIntoPieces(longitude)),
];
return some async func(data);
}
private parseHoleNumber(buffer: number[]): number[] {
if (buffer.length < 0) {
// If you don't want processing to continue in getGameShotDetail, throw an error:
throw new Error('Buffer length negative??');
// Otherwise, just return an empty array:
// return [];
}
/* tslint:disable:no-bitwise */
return buffer.filter(n => n !== 0).map(holeNumber => holeNumber >> 3);
/* tslint:enable:no-bitwise */
}
private sliceBufferIntoPieces(
buffer: number[] | string[],
chunkSize: number = 4,
): Array<number[]> {
const arr = [];
for (let i = 0; i < buffer.length; i += chunkSize) {
arr.push(buffer.slice(i, i + chunkSize));
}
if (arr.length === 0) {
// Same as above - do you want to return an empty array, or stop execution entirely?
throw new Error('Buffer empty');
}
}
关于上述两个函数,如果缓冲区为空,请考虑 - 您真的要完全停止执行,还是要继续执行空数组?如果你想停止执行,抛出一个错误(用throw
) - 否则,不要抛出,只返回一个空数组。
由于sliceBufferIntoPieces
不需要返回 Promise,因此也不需要parseCoordinate
等待它解决:
private parseCoordinate(buffer: Array<number[]>): number[] {
const itemsAreZero = (item): boolean => item === 0;
return buffer
.filter(buff => !buff.every(itemsAreZero))
.map(byte => +(this.read4byteItem(byte) / 360000).toFixed(6))
}
还要记住,Typescript 几乎总是可以推断出函数返回值的类型,而无需您显式指定它 - 除非您的 linter 迫使您注意返回类型,否则请随意忽略这些类型。
推荐阅读
- java - 多个实体的休眠搜索查询
- appium - Appium - 超时并且从未得到命令的响应
- c# - C# DataGridView ImageColumn
- docker - docker 容器中 .net core web api 应用程序中的 Https
- swift - 为什么我的 iPhone 无法将数据发送到 BLE?
- c# - Cmdlet C# 完成
- xml - Android 资源链接失败:'८dp' 与属性 layout_marginBottom (attr) 维度不兼容
- bash - 如果脚本通过管道传输到更少,bash read builtin 不会回显输入
- ios - 如何从远程音频文件中获取缓冲区?
- google-sheets - 计算 2 列中其他列中对应值的计数差异