alf*_*ino 3 javascript node.js ecmascript-6 es6-promise
在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);
});
});
};
Run Code Online (Sandbox Code Playgroud)
Promise.all解决方案(它需要一个for循环来构建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");
});
Run Code Online (Sandbox Code Playgroud)
等待循环解决方案
let inserts = [];
(async function loop() {
for (let i = 0; i < SIZE; i++) {
await insert(i, "..string..")
}
console.log("await loop ends");
})
Run Code Online (Sandbox Code Playgroud)
编辑:感谢anwsers,但我会对此进行更多研究。
await并不是真正的阻塞,我们都知道,它是在自己的代码块中阻塞。一个await循环顺序防火要求,因此,如果在中间1个请求花费更长的时间,其他的人等待它。好吧,这类似于Promise.all:如果1请求需要更长的时间,则在返回所有响应之前,不执行回调。
您的使用示例Promise.all将首先创建所有promise,然后再等待它们解决。这意味着您的请求将同时触发,并且Promise.all(...).then(thisCallback)仅当所有请求成功时才触发给定的回调。
注意:Promise.all一旦给定数组中的承诺之一被拒绝,从中返回的承诺将被拒绝。
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 endsRun Code Online (Sandbox Code Playgroud)
注意:.map对于这种情况,使用它而不是循环可能会更干净:
Promise.all(
Array.from(Array(SIZE)).map((_, i) => insert(i,"..string.."))
).then(values => {
console.log("promise all ends");
});
Run Code Online (Sandbox Code Playgroud)
await另一方面,您使用的示例等待每个诺言得到解决,然后继续执行下一个诺言:
Promise.all(
Array.from(Array(SIZE)).map((_, i) => insert(i,"..string.."))
).then(values => {
console.log("promise all ends");
});
Run Code Online (Sandbox Code Playgroud)
在上述情况下对性能的影响与它们的不同行为直接相关。
如果对您的用例“有效”意味着尽快完成请求,那么第一个示例将获胜,因为请求将在大约同一时间独立发生,而在第二个示例中,它们将以串行方式发生。
就复杂度而言,第一个示例的时间复杂度等于,O(longestRequestTime)因为请求本质上是并行发生的,因此耗时最长的请求将导致最坏的情况。
另一方面,该await示例的O(sumOfAllRequestTimes)原因是,无论单个请求花费多长时间,每个请求都必须等待上一个请求完成,因此总时间将始终包括所有请求。
简单地说,忽略运行代码的环境和应用程序引起的所有其他潜在延迟,对于1000个请求,每个请求花费1s,该Promise.all示例仍将花费〜1s,而该await示例将花费〜1000s。
也许图片会有所帮助:
注意:Promise.all实际上并不会完全并行地运行请求,通常,性能在很大程度上取决于代码运行所在的确切环境及其状态(例如事件循环),但这是一个很好的近似值。
这两种方法的主要区别在于
该await版本问题服务器的循环顺序要求。如果其中一个错误没有被捕获,则不再发出请求。如果使用 try/catch 块捕获请求错误,您可以确定哪个请求失败,并且可能以某种形式的恢复编写代码,甚至重试操作。
该Promise.all版本将以并行或接近并行的方式发出服务器请求,受浏览器对允许的最大并发请求数的限制。如果其中一个请求失败,则Promise.all返回的承诺会立即失败。如果任何请求成功并返回数据,您将丢失返回的数据。此外,如果任何请求失败,则不会取消任何未完成的请求——它们是在insert创建承诺数组时在用户代码(函数)中启动的。
正如在另一个答案中提到的,await是非阻塞的并返回到事件循环,直到其操作数承诺得到解决。thePromise.all和awaitwhile 循环版本都允许在请求正在进行时响应其他事件。
每一种都有不同的优点,这取决于我们需要哪一种来解决我们的问题。
等待循环
for(let i = 0;i < SIZE; i++){
await promiseCall();
}
Run Code Online (Sandbox Code Playgroud)
如果任何承诺被拒绝,它将并行调用所有承诺,不会对其他承诺产生任何影响。
在 ES2018 中,它对某些情况进行了简化,例如如果您只想在第一次迭代完成后才调用第二次迭代,请参阅以下示例。
async function printFiles () {
const files = await getFilePaths()
for await (const file of fs.readFile(file, 'utf8')) {
console.log(contents)
}
}
Run Code Online (Sandbox Code Playgroud)
Promise.all()
var p1 = Promise.resolve(32);
var p2 = 123;
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [32, 123, "foo"]
});
Run Code Online (Sandbox Code Playgroud)
这将顺序执行每个 Promise,并最终返回组合的循环值数组。
如果这些承诺中的任何一个被拒绝,它将仅返回该被拒绝的承诺的值。关注以下前任,
var p1 = Promise.resolve(32);
var p2 = Promise.resolve(123);
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // 123
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2080 次 |
| 最近记录: |