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

62 javascript asynchronous node.js 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.
   });
 });
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我无法将该myFunction函数声明为异步,因为我无法访问eachLimit函数的第二个回调内的值.

jib*_*jib 49

你在promise构造函数执行器函数中有效地使用了promise,所以这就是Promise构造函数的反模式.

您的代码是主要风险的一个很好的例子:不安全地传播所有错误.读那里的原因.

此外,使用async/ await可以使相同的陷阱更令人惊讶.相比:

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

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

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

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

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

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

第一个是有效的,因为Promise构造函数执行函数中的任何直接异常都会方便地拒绝新构造的promise(但.then在你自己的内部).

第二个不起作用,因为async函数中的任何立即异常都会拒绝函数本身返回async隐式promise.

由于promise构造函数执行函数的返回值未使用,这是个坏消息!

你的代码

没有理由你不能定义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.
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

虽然为什么使用过时的并发控制库await

  • 你不需要`return await`:`return new Promise`就足够了. (9认同)
  • 我正式批准这个答案,我会说完全一样:-) (2认同)

Vla*_*tko 34

我同意上面给出的答案,但有时在你的承诺中使用异步会更简洁,特别是如果你想链接几个返回承诺的操作并避免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));
Run Code Online (Sandbox Code Playgroud)
  1. 传递给Promise构造函数的函数不是异步的,因此 linter 不会显示错误。
  2. 所有异步函数都可以使用await.
  3. 可以添加自定义错误来验证异步操作的结果
  4. 错误最终被很好地捕获。

但是有一个缺点是您必须记住try/catch将其放置并附加到reject.

  • 虽然这有效,但您也可以摆脱包装承诺、try/catch 等,并对 IEFE 函数的其余部分执行相同操作:https://i.stack.imgur.com/S3pU2.png (3认同)
  • @noseratio 不能同意更多。操作虽然询问是否可以在 `Promise` 主体内使用 `async`。 (2认同)

Bry*_*ace 9

相信反模式就是反模式

异步 Promise 回调中的抛出很容易被捕获。

(async () => {
    try {
        await new Promise (async (FULFILL, BREAK) => {
            try {
                throw null;
            }
            catch (BALL) {
                BREAK (BALL);
            }
        });
    }
    catch (BALL) {
        console.log ("(A) BALL CAUGHT", BALL);
        throw BALL;
    }
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});
Run Code Online (Sandbox Code Playgroud)

或者更简单地说,

(async () => {
    await new Promise (async (FULFILL, BREAK) => {
        try {
            throw null;
        }
        catch (BALL) {
            BREAK (BALL);
        }
    });
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});
Run Code Online (Sandbox Code Playgroud)

  • 这确实是一种永远不应该使用的模式。只需删除行 `await new Promise (async (FULFILL, BREAK) => {` 并将 `BREAK` 替换为 `throw` 即可。 (2认同)
  • 实际上不,这是反模式,表明您没有完全理解 Promises 和支持的 async..await 语法。而且非常难看,尤其是`})()`的过眼线。 (2认同)