在新的 Promise() 构造函数中使用 async/await 是反模式吗?

我正在使用该函数来控制一次的最大操作数。async.eachLimit

const { eachLimit } = require("async");

function myFunction() {
 return new Promise(async (resolve, reject) => {
   eachLimit((await getAsyncArray), 500, (item, callback) => {
     // do other things that use native promises.
   }, (error) => {
     if (error) return reject(error);
     // resolve here passing the next value.
   });
 });
}

如您所见,我无法将函数声明为异步,因为我无法访问函数的第二个回调中的值。myFunctioneachLimit


答案 1

您正在承诺构造函数执行器函数中有效地使用承诺,因此这是承诺构造函数反模式

您的代码是主要风险的一个很好的例子:不能安全地传播所有错误。阅读为什么在那里

此外,使用 /可以使相同的陷阱更加令人惊讶。比较:asyncawait

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

与天真(错误)等价物:async

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

在浏览器的 Web 控制台中查找最后一个。

第一个之所以有效,是因为 Promise 构造函数执行器函数中的任何即时异常都会方便地拒绝新构造的 promise(但在任何一个中,您都是靠自己)。.then

第二个不起作用,因为函数中的任何直接异常都会拒绝异步函数本身返回的隐式承诺async

由于承诺构造函数执行器函数的返回值未使用,因此这是个坏消息!

您的代码

没有理由不能定义为:myFunctionasync

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

虽然为什么使用过时的并发控制库,当你有?await


答案 2

我同意上面给出的答案,而且,有时在你的承诺中有异步会更整洁,特别是如果你想链接几个操作返回承诺并避免地狱。在这种情况下,我会考虑使用这样的东西:then().then()

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. 传递给构造函数的函数不是异步的,因此 linters 不会显示错误。Promise
  2. 可以使用 按顺序调用所有异步函数。await
  3. 可以添加自定义错误以验证异步操作的结果
  4. 该错误最终会被很好地捕获。

但是,一个缺点是您必须记住将其放入并附加到 。try/catchreject