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


103

我正在使用该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.
   });
 });
}

如您所见,myFunction由于无法访问eachLimit函数的第二个回调中的值,因此无法将函数声明为异步。


“如您所见,我无法将myFunction声明为异步” ---您能否详细说明?
zerkms

1
哦,好的...对不起。我需要构造函数,因为我需要async.eachLimit一次避免执行500多个异步操作。我正在从文本文件下载数据并提取数据,我想避免很多异步操作,提取数据后,我必须使用数据返回一个Promise,并且我将无法从async.eachLimit的回调中返回它。 。

1.为什么需要等待?异步已经是一种控制流机制。2.如果要在
anode.js中

为了避免回调地狱,并且如果发生某些事情,外部承诺将会实现。

Answers:


88

您实际上是在promise构造函数执行程序函数内使用promise,因此这是Promise构造函数反模式

您的代码很好地说明了主要风险:没有安全地传播所有错误。读为什么在那里

此外,使用async/await会使相同的陷阱更加令人惊讶。比较:

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!

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

第一个起作用是因为Promise构造函数执行程序函数中的任何立即异常都方便地拒绝了新构造的Promise(但是.then在您自己的任何内部)。

第二个不起作用,因为async函数中的任何立即异常都会拒绝函数本身返回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


12
您不需要return awaitreturn new Promise足够。
lonesomeday

2
我正式批准了这个答案,我会说完全一样的:-)
Bergi

1
@celoxxx在这里看看。您确实不应该将async.js与promises
配合

1
@celoxxx只需删除类型,它就会变成纯js。您不应该使用async.js,因为不同的接口(节点样式的回调与Promise)会引起过多的摩擦,并导致不必要的复杂且容易出错的代码。
Bergi

1
我同意你的意见。但是这段代码很陈旧,我正在重构使用事件+ async.js(但是要控制异步的限制。如果您知道更好的方法,请说)。

21

我同意上面给出的答案,但有时,在您的诺言中保持异步有时会更整洁,尤其是如果您希望将多个返回诺言的操作链接在一起并避免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. 传递给Promise构造函数的函数不是异步的,因此linters不会显示错误。
  2. 可以使用依次调用所有异步函数await
  3. 可以添加自定义错误以验证异步操作的结果
  4. 最终可以很好地捕获该错误。

但是,缺点是您必须记住将try/catch其附加到上reject


3
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);                
        }
    })
}

删除等待和异步将解决此问题。因为您已经应用了Promise对象,这就足够了。


1
因此,在您的示例中,其axios.get(url)功能将像被称为await axios.get(url)
PrestonDocks
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.