【问题标题】:What is the correct way of writing synchronous code in a promise?在 Promise 中编写同步代码的正确方法是什么?
【发布时间】:2020-04-18 22:25:52
【问题描述】:

几天前,我在 YouTube 上观看了一段关于 Broken Promises 的有趣视频,由 James Snell 介绍。

您可以在他的repository 的视频中找到一些很好的例子。

按照他的说法,我们不应该将纯同步代码包装在 Promise 中。如果我们绝对需要一个函数来返回一个 Promise,那么使用 Promise.resolve() method 同步地解决这个 Promise。 重要的是,他还指出同步运行你的代码可以省去所有这些额外的承诺分配的麻烦

看完视频后,我查看了我的代码。并且想知道如果我做了与他在剪辑中提到的类似的事情会怎样。

让我给你举几个例子。

这是一个等待 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);
}

Promise.all() 方法数组中的前两个方法 parseHoleNumberparseShotType 的作用几乎相同。它从二进制文件中读取数据,它们最终都返回一个数字数组作为承诺。

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 所说的那样写得很好。

据我所知,所有Array 方法,包括push,都是同步的。而且我不确定从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
  • 另一个让我们使用 Promises,因为它们是最新的工具 - 我有一把新螺丝刀,让我们用它来粉刷房子
  • @JaromandaX 抱歉,如果代码让您感到不舒服。

标签: javascript node.js typescript promise


【解决方案1】:

您的代码中的所有函数都没有异步执行任何操作。一旦await somefunc 行运行,您所做的一切都是同步的,但出于某种原因,您仍将所有内容包装在Promise.all 调用中。您可以通过删除不必要的 Promise 结构和Promise.resolves 来解决这个问题(并避免视频讨论的反模式):

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 迫使您注意返回类型,否则请随意忽略这些类型。

【讨论】:

  • 感谢您的详细回复。我很感激。我也可以从somefunc() 中删除 Promise 结构吗? await somefunc() 行只是返回对象。
  • 如果 somefunc 实际上没有做任何异步操作,那么是的,让它同步。
  • 还有一件事!您认为当前的代码在不久的将来会影响应用的性能吗?
  • 仅当缓冲区过大时,但我认为不是
  • 当您需要等待异步解决问题后再做更多工作时使用异步函数和/或承诺 - 例如,等待网络请求解决时,或等待fs.readFile完成。否则,最好坚持同步代码。
猜你喜欢
  • 2010-11-22
  • 1970-01-01
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-26
相关资源
最近更新 更多