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

IT技术 javascript node.js asynchronous async-await
2021-01-12 02:00:46

我正在使用该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函数的第二个回调中的值。

4个回答

您在Promise 构造函数 executor 函数中有效地使用了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!

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

第一个有效,因为Promise 构造函数 executor 函数中的任何直接异常都方便地拒绝了新构造的 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.
    });
  });
}

但是为什么要使用过时的并发控制库await呢?

我正式批准这个答案,我会说完全一样:-)
2021-03-10 02:00:46
@celoxxx 看看这里你确实不应该使用带有Promise的 async.js
2021-03-20 02:00:46
你不需要return await:return new Promise就足够了。
2021-03-23 02:00:46
@celoxxx 只需删除类型,它就会变成普通的 js。你不应该使用 async.js,因为不同的接口 - 节点风格的回调与Promise - 会导致太多的摩擦并导致不必要的复杂和容易出错的代码。
2021-03-24 02:00:46
我同意你的看法......但是这段代码很旧,我正在重构以使用事件 + async.js(来控制异步的限制。如果你知道更好的方法,请说)。
2021-04-03 02:00:46

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

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

@noseratio 完全同意。手术虽然询问是否可以asyncPromise体内使用。
2021-03-23 02:00:46
它给出了linting错误:Promises must be handled appropriately or explicitly marked as ignored with the void operator github.com/typescript-eslint/typescript-eslint/blob/v4.28.0/...
2021-04-02 02:00:46
虽然这有效,但您最好摆脱包装Promise、try/catch 等,并对剩余的 IEFE 函数执行相同操作:i.stack.imgur.com/S3pU2.png
2021-04-08 02:00:46

相信反模式就是反模式

异步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);
});
喜欢这个哲学!
2021-04-07 02:00:46
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 对象,这就足够了。

所以在你的例子中,会axios.get(url)像被调用一样运行await axios.get(url)吗?
2021-04-04 02:00:46
不,它不会,res将包含一个Promise,其余的代码将失败,因为res.data将是未定义的。
2021-04-08 02:00:46