async/await 如何以串行和并行方式工作?

Posted

技术标签:

【中文标题】async/await 如何以串行和并行方式工作?【英文标题】:How is async/await working in serial and parallel? 【发布时间】:2018-01-10 18:30:47 【问题描述】:

我有两个async 函数。他们都在等待两个 3 秒的函数调用。但是第二个比第一个快。我认为更快的一个是并行运行,另一个是串行运行。我的假设正确吗?如果是,为什么会发生这种情况,因为这两个函数在逻辑上看起来都是一样的?

function sleep() 
  return new Promise(resolve => 
    setTimeout(resolve, 3000);
  );


async function serial() 
  await sleep();
  await sleep();


async function parallel() 
  var a = sleep();
  var b = sleep();
  await a;
  await b;


serial().then(() => 
  console.log("6 seconds over");
);

parallel().then(() => 
  console.log("3 seconds over");
);

【问题讨论】:

@Mjh Errm, setTimeout 本质上是异步的……而且 OP 代码中的几乎所有其他内容也是异步的…… @Frxstrem 除了setTimeout 根本不是异步的。它是一个同步函数,告诉事件循环稍后执行一个函数。这是一个机制,在这段代码中绝对没有异步。哪两个运动部件在不同的执行环境中以不同的速度运动?零。因此,这段代码根本不是异步的。 @Mjh 主上下文,setTimeout 的回调,serialparallel,以及对 .then 的两个调用的回调,都在不同的执行上下文中运行。跨度> @Frxstrem 这一切都在同一个线程中工作,执行上下文没有什么不同,没有两个移动部分,有问题的函数setTimeout 是同步的。模拟异步行为的机制并不意味着某些东西是异步的。但是,嘿,如果你愿意相信它是 - 我只是一个凡人,告诉你一个你不喜欢的故事。 @Vadakkumpadath 不管怎样震撼你的世界,如果你无法区分通过事件循环通知线程和实际异步操作的 机制 之间的区别,那么好的,非常好,让我们在 javascript 世界中实现异步 :) 【参考方案1】:

Frxstream 已经有an excellent answer。以此为基础,并提供一些观点:

首先要认识到,Promise 是“热”创建的——也就是说,当您拥有一个 Promise 对象时,它已经“进行中”了。

第二个重要概念是await 类似于“异步等待”——也就是说,它暂停函数的执行,直到该承诺完成。

因此,serial 函数调用sleep,返回一个承诺,然后(异步)等待该承诺完成。 3 秒后该承诺完成后,serial 再次调用sleep,得到一个承诺,然后(异步)等待该承诺完成。该承诺在 3 秒后完成后,serial 完成。

parallel 函数调用sleep 并将其promise 存储在a 中,然后调用sleep 并将其promise 存储在b 中,然后(异步)等待a 完成。在a 3 秒后完成后,parallel(异步)等待b 完成。在b 几乎立即完成后,parallel 完成。

【讨论】:

【参考方案2】:

如果您像这样编写serial 函数会更清楚:

async function serial() 
  var a = sleep(); //
  await a;         // await sleep();

  var b = sleep(); //
  await b;         // await sleep();


async function parallel() 
  var a = sleep();
  var b = sleep();
  await a;
  await b;

这里你可以清楚地看到,在serial函数中,第二个sleep()只在第一个sleep()完成后调用,而在parallel中,它在第一个完成之前立即调用,然后它等待两者都完成。因此,虽然它们可能看起来在功能上相同,但它们有细微的不同。

【讨论】:

介意你从这个问题的角度看一下:***.com/q/46889290/919480 ? @cogitoergosum - 我认为 Frxstrem 只是在解释您在问题中提出的不同之处,而不是建议您必须这样做。如果您希望操作连续运行(一个接一个),上面的serial 很好。上面的parallel 确实可以更好地实现为await Promise.all([sleep(), sleep()]);,但同样,我认为这不是上面答案的重点。上面的答案(我认为)的重点是解释问题的时间差异。 另外,var a = sleep(); var b = sleep(); await a; await b; 也一样。【参考方案3】:

因为sleep()函数是一个同步函数,它只是返回一个异步promise,例如:

function sleep (time) 
    return new Promise((resolve) => setTimeout(resolve, time));

parallel()中,两个sleep()同步初始化两个promise,同时等待resolve,大概3s左右。

但是,在serial() 中,两个await sleep() 意味着第二个sleep() 承诺必须等待第一个sleep() 被解析,所以大约需要6s。

【讨论】:

【参考方案4】:

串行运行:一个接一个地运行函数

// ? It’s slow! and use only for serial(one after another) run
async function doThings() 
    const thing1 = await asyncThing1(); // waits until resolved/rejected
    console.log(thing1);

    const thing2 = await asyncThing2(); // starts only after asyncThing1() has finished.
    console.log(thing2);


doThings();

并行运行:并行运行函数

// ✅ async code is run in parallel!
async function doThings() 
    const p1 = asyncThing1(); // runs parallel
    const p2 = asyncThing2(); // runs parallel


    // Method 1: Prefer => Promise.all()
    const [resultThing1, resultThing2] = await Promise.all([p1, p2]); // waits for all 
    // the promises get resolved or fails fast (If one of the promises supplied to it 
    // rejects, then the entire thing rejects).

    // Method 2: Not-Preferred
    const resultThing1 = await p1;
    const resultThing2 = await p2;

    console.log(resultThing1); // reaches here only after both the p1 & p2 have completed.
    console.log(resultThing2);


doThings();

【讨论】:

以上是关于async/await 如何以串行和并行方式工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何并行使用 Swift async/await

JavaScript 工作原理之四-事件循环及异步编程的出现和 5 种更好的 async/await 编程方式(译)

进阶篇:以IL为剑,直指async/await

Swift 并行编程现状和展望 - async/await 和参与者模式

如何正确设置 HttpClient 的延续?

javascript 按顺序和并行处理async / await循环