等待循环vs Promise.all [英] await loop vs Promise.all

查看:82
本文介绍了等待循环vs Promise.all的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在db上有一组异步操作要做,我想知道执行阻塞" await循环与Promise.all的性能有什么区别.

let insert = (id,value) => {
    return new Promise(function (resolve, reject) {
        connnection.query(`insert into items (id,value) VALUES (${id},"${value}")`, function (err, result) {
            if (err) return reject(err)
                return resolve(result);
        });
    });
};

Promise.all解决方案(它需要一个for循环来构建promise数组.)

let inserts = [];
for (let i = 0; i < SIZE; i++) inserts.push(insert(i,"..string.."))
Promise.all(inserts).then(values => { 
    console.log("promise all ends");
});

等待循环解决方案

let inserts = [];
(async function loop() {
    for (let i = 0; i < SIZE; i++) {
        await insert(i, "..string..")
    }
    console.log("await loop ends");
})

谢谢anwsers,但我将对此进行深入研究. await并不是真正的阻塞,我们都知道,它在自己的代码块中处于阻塞状态. await循环顺序触发请求,因此如果在中间1个请求中花费的时间更长,则其他请求将等待它. 好吧,这类似于Promise.all:如果1个请求需要更长的时间,则在返回所有响应之前,将不执行回调.

解决方案

您使用Promise.all的示例将首先创建所有promise,然后再等待它们解决.这意味着您的请求将同时触发,并且Promise.all(...).then(thisCallback)的回调仅在所有请求均成功的情况下触发.

注意:从Promise.all返回的promise将在给定数组中的promise之一拒绝后立即拒绝.

 const SIZE = 5;
const insert = i => new Promise(resolve => {
  console.log(`started inserting ${i}`);
  setTimeout(() => {
    console.log(`inserted ${i}`);
    resolve();
  }, 300);
});

// your code
let inserts = [];
for (let i = 0; i < SIZE; i++) inserts.push(insert(i, "..string.."))
Promise.all(inserts).then(values => {
  console.log("promise all ends");
});

// requests are made concurrently

// output
// started inserting 0
// started inserting 1
// started inserting 2
// ...
// started inserting 4
// inserted 0
// inserted 1
// ...
// promise all ends 

注意:在这种情况下,使用.map而不是循环可能更干净:

Promise.all(
  Array.from(Array(SIZE)).map((_, i) => insert(i,"..string.."))
).then(values => { 
    console.log("promise all ends");
});

您使用await的示例在等待并解雇下一个之前等待每个诺言得到解决:

 const SIZE = 5;
const insert = i => new Promise(resolve => {
  console.log(`started inserting ${i}`);
  setTimeout(() => {
    console.log(`inserted ${i}`);
    resolve();
  }, 300);
});

let inserts = [];
(async function loop() {
  for (let i = 0; i < SIZE; i++) {
    await insert(i, "..string..")
  }
  console.log("await loop ends");
})()

// no request is made until the previous one is finished

// output
// started inserting 0
// inserted 0
// started inserting 1
// ...
// started inserting 4
// inserted 4
// await loop ends 

以上情况对绩效的影响与他们不同的行为直接相关.

如果对您的用例而言高效"意味着尽快完成请求,则第一个示例将获胜,因为这些请求将在大约同一时间独立发生,而在第二个示例中,它们将在同一时间发生.连续时尚.

就复杂度而言,第一个示例的时间复杂度等于O(longestRequestTime),因为请求本质上是并行发生的,因此花费时间最长的请求将导致最坏的情况.

另一方面,await示例具有O(sumOfAllRequestTimes),因为无论单个请求花费多长时间,每个请求都必须等待上一个请求完成,因此总时间将始终包括所有这些请求./p>

将所有事物都考虑在内,而不考虑由于运行代码的环境和应用程序造成的所有其他潜在延迟,对于1000个请求,每个请求花费1s,Promise.all示例仍然花费〜1s,而await例如,大约需要1000秒.

也许图片会有所帮助:

注意:Promise.all实际上不会完全并行地运行请求,并且总体而言,性能将很大程度上取决于代码运行所在的确切环境及其状态(例如事件循环) ),但这是一个很好的近似值.

Having a set of async operations on db to do, I'm wondering what's the difference performance-wise of doing a "blocking" await loop versus a Promise.all.

let insert = (id,value) => {
    return new Promise(function (resolve, reject) {
        connnection.query(`insert into items (id,value) VALUES (${id},"${value}")`, function (err, result) {
            if (err) return reject(err)
                return resolve(result);
        });
    });
};

Promise.all solution (it needs a for loop to builds the array of promises..)

let inserts = [];
for (let i = 0; i < SIZE; i++) inserts.push(insert(i,"..string.."))
Promise.all(inserts).then(values => { 
    console.log("promise all ends");
});

await loop solution

let inserts = [];
(async function loop() {
    for (let i = 0; i < SIZE; i++) {
        await insert(i, "..string..")
    }
    console.log("await loop ends");
})

Edit: thanks for the anwsers, but I would dig into this a little more. await is not really blocking, we all know that, it's blocking in its own code block. An await loop sequentially fire requests, so if in the middle 1 requests takes longer, the other ones waits for it. Well this is similar to Promise.all: if a 1 req takes longer, the callback is not executed until ALL the responses are returned.

解决方案

Your example of using Promise.all will create all promises first before waiting for them to resolve. This means that your requests will fire concurrently and the callback given to Promise.all(...).then(thisCallback) will only fire if all requests were successful.

Note: promise returned from Promise.all will reject as soon as one of the promises in the given array rejects.

const SIZE = 5;
const insert = i => new Promise(resolve => {
  console.log(`started inserting ${i}`);
  setTimeout(() => {
    console.log(`inserted ${i}`);
    resolve();
  }, 300);
});

// your code
let inserts = [];
for (let i = 0; i < SIZE; i++) inserts.push(insert(i, "..string.."))
Promise.all(inserts).then(values => {
  console.log("promise all ends");
});

// requests are made concurrently

// output
// started inserting 0
// started inserting 1
// started inserting 2
// ...
// started inserting 4
// inserted 0
// inserted 1
// ...
// promise all ends

Note: It might be cleaner to use .map instead of a loop for this scenario:

Promise.all(
  Array.from(Array(SIZE)).map((_, i) => insert(i,"..string.."))
).then(values => { 
    console.log("promise all ends");
});

Your example of using await on the other hand, waits for each promise to resolve before continuing and firing of the next one:

const SIZE = 5;
const insert = i => new Promise(resolve => {
  console.log(`started inserting ${i}`);
  setTimeout(() => {
    console.log(`inserted ${i}`);
    resolve();
  }, 300);
});

let inserts = [];
(async function loop() {
  for (let i = 0; i < SIZE; i++) {
    await insert(i, "..string..")
  }
  console.log("await loop ends");
})()

// no request is made until the previous one is finished

// output
// started inserting 0
// inserted 0
// started inserting 1
// ...
// started inserting 4
// inserted 4
// await loop ends

The implications for performance in the above cases are directly correlated to their different behavior.

If "efficient" for your use case means to finish up the requests as soon as possible, then the first example wins because the requests will be happening around the same time, independently, whereas in the second example they will happen in a serial fashion.

In terms of complexity, the time complexity for your first example is equal to O(longestRequestTime) because the requests will happen essentially in parallel and thus the request taking the longest will drive the worst-case scenario.

On the other hand, the await example has O(sumOfAllRequestTimes) because no matter how long individual requests take, each one has to wait for the previous one to finish and thus the total time will always include all of them.

To put things in numbers, ignoring all other potential delays due to the environment and application in which the code is ran, for 1000 requests, each taking 1s, the Promise.all example would still take ~1s while the await example would take ~1000s.

Maybe a picture would help:

Note: Promise.all won't actually run the requests exactly in parallel and the performance in general will greatly depend on the exact environment in which the code is running and the state of it (for instance the event loop) but this is a good approximation.

这篇关于等待循环vs Promise.all的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆