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

Is it an anti-pattern to use async/await inside of a new Promise() constructor?

提问人: 提问时间:3/27/2017 最后编辑:Alexis Tyler 更新时间:6/19/2023 访问量:141450

问:

我正在使用该函数来控制一次的最大操作数。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

JavaScript 节点.js 异步 async-await

评论

0赞 zerkms 3/27/2017
“如您所见,我无法将 myFunction 声明为异步”---您能详细说明一下吗?
1赞 3/27/2017
哦,好吧......不好意思。我需要构造函数,因为我需要 async.eachLimit 来避免一次超过 500 个异步操作。我正在从文本文件下载和提取数据,我想避免太多异步操作,提取数据后,我必须返回带有数据的 Promise,并且我无法从 async.eachLimit 的回调中返回它。
0赞 slebetman 3/27/2017
1.为什么需要等待?异步已经是一种控制流机制。2. 如果你想在 node.js 中使用带有 promise 的 async.js,请查看 async-q
0赞 3/27/2017
为了避免回调地狱,如果有什么东西抛出,外部承诺就会被抓住。

答:

134赞 jib 3/27/2017 #1

您有效地在 promise 构造函数执行器函数中使用了 promise,因此这是 Promise 构造函数反模式

您的代码是主要风险的一个很好的示例:不能安全地传播所有错误。阅读原因

此外,使用/可以使相同的陷阱更加令人惊讶。比较: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

由于 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.
    });
  });
}

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

评论

17赞 lonesomeday 3/28/2017
你不需要:就足够了。return awaitreturn new Promise
2赞 Bergi 3/28/2017
我正式批准这个答案,我会说完全一样的:-)
1赞 Bergi 3/28/2017
@celoxxx 请看这里。你确实不应该将 async.js 与 promise 一起使用
1赞 Bergi 3/28/2017
@celoxxx 只需删除类型,它就会变成普通的 js。您不应该使用 async.js,因为不同的接口(节点样式回调与承诺)会导致太多摩擦,并导致不必要的复杂且容易出错的代码。
1赞 3/28/2017
我同意你的看法......但是这段代码很旧,我正在重构以使用事件 + async.js(来控制异步的限制。如果您知道更好的方法,请说)。
-6赞 Alain 4/16/2020 #2
static getPosts(){
    return new Promise( (resolve, reject) =>{
        try {
            const res =  axios.get(url);
            const data = res.data;
            resolve(
                data.map(post => ({
                    ...post,
                    createdAt: new Date(post.createdAt)
                }))
            )
        } catch (err) {
            reject(err);                
        }
    })
}

删除 await 和 async 将解决此问题。因为你已经应用了 Promise 对象,这就足够了。

评论

1赞 PrestonDocks 10/24/2020
因此,在您的示例中,将像调用 ?axios.get(url)await axios.get(url)
2赞 CherryDT 3/1/2021
不,它不会,将包含一个承诺,其余代码将失败,因为将未定义。resres.data
0赞 ggorlen 3/2/2023
此代码已损坏且具有误导性。建议删除。即使你可以神奇地用 a 避免 /,它仍然比只使用 / 更丑陋。asyncawaitnew Promise()asyncawait
68赞 Vladyslav Zavalykhatko 4/30/2020 #3

根据 @Bergi 给出的反馈,我想强调的是,我同意上面给出的答案,宁愿选择不同的方法。

但是,如果您需要在 promise 中加入异步,我会考虑使用如下内容:

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. 传递给构造函数的函数不是异步的,因此 linter 不会显示错误。Promise
  2. 所有异步函数都可以使用 按顺序调用。await
  3. 可以添加自定义错误来验证异步操作的结果
  4. 错误最终被很好地捕获。

但缺点是您必须记住将其放置并附加到 .try/catchreject

评论

0赞 GorvGoyl 7/4/2021
它给出了 linting 错误:github.com/typescript-eslint/typescript-eslint/blob/v4.28.0/...Promises must be handled appropriately or explicitly marked as ignored with the void operator
3赞 noseratio 7/10/2021
虽然这可行,但你不妨去掉包装承诺、try/catch 等,并对你的 IEFE 函数的其余部分做同样的事情:i.stack.imgur.com/S3pU2.png
3赞 Vladyslav Zavalykhatko 7/10/2021
@noseratio完全同意。OP询问是否可以在体内使用。asyncPromise
0赞 Marcelo Scofano Diniz 11/11/2022
这个答案是对 jib 的答案的完美补充,正是我需要理解的。
0赞 Bergi 5/2/2023
@VladyslavZavalykhatko 如果您同意鼻子比,应该清楚在 .它甚至不“整洁”。asyncnew Promise
9赞 Bryan Grace 2/1/2021 #4

相信反模式是一种反模式

异步 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);
});

或者更简单地说,

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

评论

2赞 Bergi 2/18/2023
这确实是一种永远不应该使用的模式。只需删除该行并替换为 .await new Promise (async (FULFILL, BREAK) => {BREAKthrow
2赞 Mulan 2/23/2023
实际上不,这是反模式,表明您不完全理解 Promise 和支持异步。await 语法。而且它非常丑陋,尤其是}) ().
1赞 ggorlen 3/2/2023
“相信反模式是一种反模式”听起来像是试图为了它而逆向而行逆。在这样的承诺之上添加毫无意义的包装器会使代码客观上更加混乱,并且在很大程度上是一种反模式。即使这是很好的做法,JS 也不是 COBOL,因此在我所知道的任何 PR 或 linter 中,以所有大写字母命名变量和调用永远不会飞。(resolve, reject)(FULFILL, BREAK)
0赞 limeandcoconut 6/8/2023
@ggorlen--onLLMstrike 拥有它的权利。
1赞 tobiasBora 1/19/2022 #5

我没有通过阅读其他答案直接意识到这一点,但重要的是评估你的异步函数以将其转换为 Promise

因此,如果您使用如下方法定义异步函数:

let f = async () => {
    // ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) .. 
};

你把它变成一个承诺,使用:

let myPromise = f()

然后,您可以将 is 作为 Promise 进行操作,例如使用 ...Promise.all([myPromise])

当然,您可以使用以下方法将其变成单衬垫

(async () => { code with await })()

评论

0赞 Pipo 8/31/2023
这应该是最重要的答案,它实际上是你想用新的 Promise(async) 东西做什么,你只想存储异步并调用它,这是一样的谢谢你睁开我的眼睛哈哈