等待Promise.all()和多次等待之间有什么区别?

2020/10/18 23:21 · javascript ·  · 0评论

之间有什么区别:

const [result1, result2] = await Promise.all([task1(), task2()]);

const t1 = task1();
const t2 = task2();

const result1 = await t1;
const result2 = await t2;

const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];

注意事项

这个答案仅涵盖了await系列和系列之间的时序差异Promise.all请务必阅读@mikep的综合答案,其中也涵盖了错误处理方面更重要的区别


出于此答案的目的,我将使用一些示例方法:

  • res(ms) 是一个函数,它需要一个整数毫秒,并返回一个承诺,该承诺将在该毫秒后解析。
  • rej(ms) 是一个函数,它需要一个整数毫秒,并返回一个承诺,该承诺将在该毫秒后被拒绝。

调用将res启动计时器。Promise.all在所有延迟完成后,可以使用等待一些延迟来解决,但请记住它们是同时执行的:

例子1

const data = await Promise.all([res(3000), res(2000), res(1000)])
//                              ^^^^^^^^^  ^^^^^^^^^  ^^^^^^^^^
//                               delay 1    delay 2    delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O           delay 2
// =========O                     delay 3
//
// =============================O Promise.all

这意味着Promise.all将在3秒后使用内部承诺中的数据进行解析。

但是,Promise.all具有“快速失败”的行为

范例#2

const data = await Promise.all([res(3000), res(2000), rej(1000)])
//                              ^^^^^^^^^  ^^^^^^^^^  ^^^^^^^^^
//                               delay 1    delay 2    delay 3
//
// ms ------1---------2---------3
// =============================O delay 1
// ===================O           delay 2
// =========X                     delay 3
//
// =========X                     Promise.all

如果async-await改为使用,则必须等待每个promise依次解决,这可能不那么有效:

例子#3

const delay1 = res(3000)
const delay2 = res(2000)
const delay3 = rej(1000)

const data1 = await delay1
const data2 = await delay2
const data3 = await delay3

// ms ------1---------2---------3
// =============================O delay 1
// ===================O           delay 2
// =========X                     delay 3
//
// =============================X await

第一个区别-快速失败

我同意@zzzzBov的回答,但是Promise.all唯一的区别不是“快速失败”优势评论中的一些用户问Promise.all,如果在否定情况下(某些任务失败时)使用速度更快,为什么值得这样做。我问,为什么不呢?如果我有两个独立的异步并行任务,而第一个要花费很长的时间才能解决,而第二个任务却要在很短的时间内被拒绝,为什么要让用户等待更长的调用才能收到错误消息呢?在实际应用中,我们必须考虑负面情况。但是可以-在这第一个区别中,您可以决定使用哪个替代方法:Promise.allvs await

第二点不同-错误处理

但是,在考虑错误处理时,您必须使用Promise.all无法正确处理由多个awaits触发的异步并行任务的错误在否定情况下,无论在何处使用try / catch,都将始终以UnhandledPromiseRejectionWarning结尾PromiseRejectionHandledWarning这就是为什么Promise.all设计的原因当然有人会说我们可以使用来抑制这些错误process.on('unhandledRejection', err => {})process.on('rejectionHandled', err => {})但这不是一个好习惯。我在互联网上发现了许多示例,这些示例根本不考虑针对两个或多个独立异步并行任务的错误处理,或者以错误的方式进行考虑-仅使用try / catch并希望它会捕获错误。在这方面几乎找不到好的做法。

概要

TL; DR:await对于两个或多个独立的异步并行任务,请勿使用多个,因为您将无法正确处理错误。始终使用Promise.all()此用例。

Async /await不是Promises的替代品,它只是使用Promise的一种不错的方法。异步代码以“同步样式”编写,我们可以避免then在promise中使用多个

有人说,使用时Promise.all()我们不能单独处理任务错误,而只能处理第一个被拒绝的承诺中的错误(分开处理可能有用,例如用于日志记录)。这不是问题-请参阅此答案底部的“添加”标题。

例子

考虑这个异步任务...

const task = function(taskNum, seconds, negativeScenario) {
  return new Promise((resolve, reject) => {
    setTimeout(_ => {
      if (negativeScenario)
        reject(new Error('Task ' + taskNum + ' failed!'));
      else
        resolve('Task ' + taskNum + ' succeed!');
    }, seconds * 1000)
  });
};

当您在积极的情况下运行任务时,sPromise.all与multiple之间没有区别await两个示例均Task 1 succeed! Task 2 succeed!在5秒后结束

// Promise.all alternative
const run = async function() {
  // tasks run immediate in parallel and wait for both results
  let [r1, r2] = await Promise.all([
    task(1, 5, false),
    task(2, 5, false)
  ]);
  console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!
// multiple await alternative
const run = async function() {
  // tasks run immediate in parallel
  let t1 = task(1, 5, false);
  let t2 = task(2, 5, false);
  // wait for both results
  let r1 = await t1;
  let r2 = await t2;
  console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: Task 1 succeed! Task 2 succeed!

但是,当第一个任务花费10秒并成功完成,而第二个任务花费5秒但失败时,发出的错误有所不同。

// Promise.all alternative
const run = async function() {
  let [r1, r2] = await Promise.all([
      task(1, 10, false),
      task(2, 5, true)
  ]);
  console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// multiple await alternative
const run = async function() {
  let t1 = task(1, 10, false);
  let t2 = task(2, 5, true);
  let r1 = await t1;
  let r2 = await t2;
  console.log(r1 + ' ' + r2);
};
run();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!

我们已经在这里注意到,当await并行使用多个s时,我们做错了让我们尝试处理错误:

// Promise.all alternative
const run = async function() {
  let [r1, r2] = await Promise.all([
    task(1, 10, false),
    task(2, 5, true)
  ]);
  console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: Caught error Error: Task 2 failed!

如您所见,要成功处理错误,我们只需要向run函数添加一个catch并将带有catch逻辑的代码添加到回调中。我们不需要处理run函数内部的错误,因为异步函数会自动执行此操作-答应拒绝该task函数会导致函数被拒绝run

为了避免回调,我们可以使用“同步样式”(async / await+ try / catch),
try { await run(); } catch(err) { }

但是在此示例中这是不可能的,因为我们不能
await在主线程中使用-它只能在异步函数中使用(因为没人想要)阻止主线程)。要测试处理是否以“同步样式”工作,我们可以run从另一个异步函数调用该函数,或者使用IIFE(立即调用函数表达式:MDN):

(async function() { 
  try { 
    await run(); 
  } catch(err) { 
    console.log('Caught error', err); 
  }
})();

这是运行两个或多个异步并行任务并处理错误的唯一正确方法。您应避免使用以下示例。

错误的例子

// multiple await alternative
const run = async function() {
  let t1 = task(1, 10, false);
  let t2 = task(2, 5, true);
  let r1 = await t1;
  let r2 = await t2;
  console.log(r1 + ' ' + r2);
};

我们可以尝试以几种方式处理上面代码中的错误...

try { run(); } catch(err) { console.log('Caught error', err); };
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled 

...什么也没抓住,因为它处理同步代码但是run异步的。

run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

嗯?我们首先看到任务2的错误未得到处理,后来又被捕获。误导性控制台中仍然充满错误,这种方式仍然无法使用。

(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: Caught error Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

...与上述相同。用户@Qwerty在其已删除的答案中询问了这种奇怪的行为,在该行为中似乎捕获了错误但也未解决。我们捕获错误,因为run()await关键字行中拒绝了因为并且可以在调用时使用try / catch捕获run()我们还会收到未处理的错误,因为我们正在同步调用异步任务函数(没有await关键字),并且此任务在run()函数外部运行并失败

这类似于在调用一些调用setTimeout的同步函数时无法通过try / catch处理错误的情况:

function test() {
  setTimeout(function() { 
    console.log(causesError); 
    }, 0);
}; 
try { 
  test(); 
} catch(e) { 
  /* this will never catch error */ 
}`.

另一个不好的例子:

const run = async function() {
  try {
    let t1 = task(1, 10, false);
    let t2 = task(2, 5, true);
    let r1 = await t1;
    let r2 = await t2;
  }
  catch (err) {
    return new Error(err);
  }
  console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Caught error', err); });
// at 5th sec: UnhandledPromiseRejectionWarning: Error: Task 2 failed!
// at 10th sec: PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

...“只有”两个错误(缺少第三个错误),但没有发现任何错误。

加法(处理单独的任务错误以及首次失败错误)

const run = async function() {
  let [r1, r2] = await Promise.all([
    task(1, 10, true).catch(err => { console.log('Task 1 failed!'); throw err; }),
    task(2, 5, true).catch(err => { console.log('Task 2 failed!'); throw err; })
  ]);
  console.log(r1 + ' ' + r2);
};
run().catch(err => { console.log('Run failed (does not matter which task)!'); });
// at 5th sec: Task 2 failed!
// at 5th sec: Run failed (does not matter which task)!
// at 10th sec: Task 1 failed!

...请注意,在此示例中,我拒绝了这两个任务,以更好地演示发生了什么(throw err用于引发最终错误)。

通常,使用Promise.all()运行会并行请求“异步”。使用await可以并行运行OR是“同步”阻断。

下面的test1test2函数显示了如何await运行异步或同步。

test3显示Promise.all()这是异步的。

具有定时结果的jsfiddle- 打开浏览器控制台以查看测试结果

同步行为。不并行运行,大约需要1800ms

const test1 = async () => {
  const delay1 = await Promise.delay(600); //runs 1st
  const delay2 = await Promise.delay(600); //waits 600 for delay1 to run
  const delay3 = await Promise.delay(600); //waits 600 more for delay2 to run
};

异步行为。在运行相同常,大约需要600毫秒

const test2 = async () => {
  const delay1 = Promise.delay(600);
  const delay2 = Promise.delay(600);
  const delay3 = Promise.delay(600);
  const data1 = await delay1;
  const data2 = await delay2;
  const data3 = await delay3; //runs all delays simultaneously
}

异步行为。并行运行,大约需要600毫秒

const test3 = async () => {
  await Promise.all([
  Promise.delay(600), 
  Promise.delay(600), 
  Promise.delay(600)]); //runs all delays simultaneously
};

TLDR;如果您使用Promise.all()它,它还将“快速失败”-在任何包含的功能首次失败时停止运行

您可以自己检查。

在这个小提琴中,我进行了一个测试,以证明的阻止性质,与之await相反Promise.all,它将开始所有的承诺,而在一个等待时它将与其他承诺继续进行。

如果等待Promise.all([task1(),task2()]); “ task1()”和“ task2()”将并行运行,并等待两个诺言都完成(已解决或被拒绝)。鉴于

const result1 = await t1;
const result2 = await t2;

t2仅在t1完成执行(已解决或拒绝)后运行。t1和t2都不会并行运行。

本文地址:http://javascript.askforanswer.com/dengdaipromise-allheduocidengdaizhijianyoushenmequbie.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!