【问题标题】:Is it an anti-pattern to use async/await inside of a new Promise() constructor?在新的 Promise() 构造函数中使用 async/await 是一种反模式吗?
【发布时间】:2017-08-19 13:26:16
【问题描述】:

我正在使用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 声明为异步”---你能详细说明一下吗?
  • 哦,好吧...对不起。我需要构造函数,因为我需要 async.eachLimit 来避免一次超过 500 个异步操作。我正在从文本文件中下载和提取数据,我想避免太多的异步操作,在我提取数据后,我必须返回一个带有数据的 Promise,我将无法从 async.eachLimit 的回调中返回它.
  • 1.为什么需要等待?异步已经是一种控制流机制。 2. 如果你想在 node.js 中使用带有 Promise 的 async.js,请查看 async-q
  • 为了避免回调地狱,如果有东西抛出,外部承诺会捕获。

标签: javascript node.js asynchronous async-await


【解决方案1】:

相信反模式是一种反模式

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

(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】:

我同意上面给出的答案,但有时在你的 Promise 中包含 async 会更整洁,特别是如果你想链接多个返回 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

【讨论】:

【解决方案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);                
        }
    })
}

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

【讨论】:

  • 所以在你的例子中,axios.get(url) 会像被称为 await axios.get(url) 一样运行吗?
  • 不,它不会,res 将包含一个承诺,其余代码将失败,因为 res.data 将是未定义的。
【解决方案4】:

您在 Promise 构造函数执行器函数中有效地使用了 Promise,所以这是 Promise constructor anti-pattern

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

另外,使用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 构造函数执行器函数中的任何 立即 异常都会方便地拒绝新构造的 Promise(但在任何 .then 中,您只能靠自己)。

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

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

您的代码

没有理由不能将myFunction 定义为async

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,为什么还要使用过时的并发控制库?

【讨论】:

  • 你不需要return awaitreturn new Promise就足够了。
  • 我正式批准这个答案,我会说完全一样的:-)
  • @celoxxx 看看here。你确实不应该将 async.js 与 promises 一起使用
  • @celoxxx 只需删除类型,它就会变成纯 js。你不应该使用 async.js,因为不同的接口——节点风格的回调与承诺——会造成太多的摩擦,并导致不必要的复杂和容易出错的代码。
  • 我同意你的观点......但是这段代码很旧,我正在重构使用 events + async.js (来控制异步的限制,但是。如果你知道更好的方法,请说)。
猜你喜欢
  • 1970-01-01
  • 2016-07-21
  • 1970-01-01
  • 2021-04-05
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 2016-01-27
相关资源
最近更新 更多