履行承诺和等待一切

时间:2018-08-19 10:15:11

标签: javascript asynchronous promise async-await

在所有承诺均得到解决的假设下,异步迭代(for-await-of循环)比使用Promise.all更快吗?

来自specification on asynchronous iteration

  

每次访问序列中的下一个值时,我们都会隐式await从迭代器方法返回的承诺。

使用异步迭代:

let pages = [fetch('/echo/json/'), fetch('/echo/html/'), fetch('/echo/xml/')]
for await (let page of pages) {
    console.log(page)
}

使用Promise.all

let pages = await Promise.all([fetch('/echo/json/'), fetch('/echo/html/'), fetch('/echo/xml/')])

pages.forEach(page => console.log(page))

它们两者都是并行获取页面的,但是我想知道异步迭代是否在所有页面完成获取之前开始循环。我曾尝试在浏览器的devtools中限制网络,以模拟这种情况,但任何差异仍然很少被注意到。

3 个答案:

答案 0 :(得分:4)

  

异步迭代(for-await-of循环)比使用Promise.all快吗?

不。当最后一个承诺解决时,循环和Promise.all都将完成,这几乎是同时发生的。如果最后要解决的承诺是数组中的第一个承诺,则Promise.all会立即完成,而循环仍必须迭代其他元素,这可能会导致少量开销,但这无关紧要。唯一真正重要的情况是:

如果其中一个承诺被拒绝,Promise.all会立即退出,而循环必须达到该承诺。

  

我想知道异步迭代是否在所有页面完成提取之前就开始循环。

是的,您可以更早地获得第一个结果,但是所有结果将同时可用。如果您想向用户显示数据,则使用for循环是有意义的,因为他可以在其余数据仍在获取的同时开始读取数据;但是,如果您想累积数据,则等待它们都是有意义的,因为这简化了迭代逻辑。

答案 1 :(得分:0)

我认为您不需要await,这也不是获取数据并将其写入控制台的最快方法:

let pages = Promise.all([fetch('/echo/json/'), fetch('/echo/html/'), fetch('/echo/xml/')])
pages.then((page) => {console.log(page)});

因为pages.then()将等待每个诺言的兑现。

但是您可以像上面那样异步获取数据。并将它们写入控制台,而无需等待页面。像这样:

 var sequence = Promise.resolve();
 ['/echo/json/','/echo/html/','/echo/xml/'].forEach(function(url) {
   sequence.then(function() {
      return fetch(url);
    })
    .then((data) => {console.log(data)});
  });

但是上面的代码没有考虑页面顺序。如果页面顺序是您的事。 您可以尝试一下,这是获取数据并按顺序显示它们的最快方法:

var sequence = Promise.resolve();

      // .map executes all of the network requests immediately.
      var arrayOfExecutingPromises =  
      ['/echo/json/','/echo/html/','/echo/xml/'].map(function(url) {
        return fetch(url);
      });

     arrayOfExecutingPromises.forEach(function (request) {
    // Loop through the pending requests that were returned by .map (and are in order) and
    // turn them into a sequence.
    // request is a fetch() that's currently executing.
    sequence = sequence.then(function() { 
      return request.then((page) => {console.log('page')});
    });
  });

答案 2 :(得分:0)

问题的一部分是,如果你在一个 Promise 数组上使用 for-await-of,你会按照指定的顺序迭代它,如果给定数组中的下一个 Promise 在前一个之前解决,这无关紧要:

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

(async function () {
    const arr = [
        sleep(2000).then(() => 'a'),
        'x',
        sleep(1000).then(() => 'b'),
        'y',
        sleep(3000).then(() => 'c'),
        'z',
    ];

    for await (const item of arr) {
        console.log(item);
    }
}());

输出:

➜  firstcomefirstserved git:(main) node examples/for-await-simple.js 
a
x
b
y
c
z

但有时 - 就像在您的问题中一样,您希望在承诺产生结果后立即处理结果。因此,我决定编写一个异步迭代器,使 for await首先承诺先解决的方式工作。代码如下:

// promises is an Array not an Iterable
async function* auxIterator(promises) {
  let wrappedPromises = promises.map((p, i) => {
    return new Promise((resolve, reject) => {
      p.then(r => resolve([r,i]))
      .catch(e => reject(e))
    })
  });

  let [r, i] = await Promise.race(wrappedPromises);
  yield r;
  promises.splice(i,1);
  if (promises.length) 
    yield * auxIterator(promises)
}

async function * frstcmfrstsvd(promises) {
  let wrappedPromises = promises.map((p, i) => {
    return new Promise((resolve, reject) => {
      Promise.resolve(p).then(r => resolve({value: r, index: i, status: 'fulfilled'}))
      .catch(e => resolve({reason: e, index: i, status: 'rejected'}))
    })
  });

  yield * await auxIterator(wrappedPromises);
}
export default frstcmfrstsvd

您可以在 npm 包 frstcmfrstsvd 中找到完整代码。

尚未进行详尽的测试,但乍一看,Promise.allSettled 的性能似乎要好一些:

> node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 323.104ms
allsettled: 317.319ms
> node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 327.142ms
allsettled: 315.415ms
> node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 322.753ms
allsettled: 318.955ms
> node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 325.562ms
allsettled: 317.375ms
> node examples/performance-reject-frstcmfrstsvd.mjs
frstcmfrstsvd: 322.25ms
allsettled: 318.09ms

查看文件 examples/performance-reject-frstcmfrstsvd.mjs

因此,所有这些实验的结论似乎是,正如@jonas-wilms 所说,它们大致同时完成