与 Promise.all 并行操作?
Parallel operations with Promise.all?
我相信 Promise.all 会并行执行您传递给它的所有函数,并且不关心返回的承诺完成的顺序。
但是当我写这段测试代码时:
function Promise1(){
return new Promise(function(resolve, reject){
for(let i = 0; i < 10; i++){
console.log("Done Err!");
}
resolve(true)
})
}
function Promise2(){
return new Promise(function(resolve, reject){
for(let i = 0; i < 10; i++){
console.log("Done True!");
}
resolve(true)
})
}
Promise.all([
Promise1(),
Promise2()
])
.then(function(){
console.log("All Done!")
})
我得到的结果是这样的
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done!
但是如果它们是 运行 并行的,我不希望它们同时执行并给我这样的结果吗?
Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Etc. Etc.?
或者我在做这件事的过程中遗漏了什么?
那是因为你的 Promise 是阻塞同步的!尝试使用超时而不是同步循环:
function randomResolve(name) {
return new Promise(resolve => setTimeout(() => {
console.log(name);
resolve();
}, 100 * Math.random()));
}
Promise.all([
randomResolve(1),
randomResolve(2),
randomResolve(3),
randomResolve(4),
])
.then(function(){
console.log("All Done!")
})
A non-Async body 串行执行
当您在 body 中进行异步调用时(例如,点击 URL),数组中的其他 Promise 将开始执行。
在 Johannes Merz 开始的基础上,我提出这段代码来阐明事情是并行发生的。
JS 是单线程的,但是 Node.js 有很多工具可以显式和隐式地启动额外的线程。 Promises 公开了我们经常需要的更多功能,而无需显式启动新线程或进程。 Promise.all() 就是这样一个例子,但是您需要熟悉 Promises 才能使用它而不会给自己带来严重的麻烦,例如 Promise Scope 内存泄漏。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(() => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf();
Promise.all([
randomResolve(1, 1000 * Math.random()),
randomResolve(2, 1000 * Math.random()),
randomResolve(3, 1000 * Math.random()),
randomResolve(4, 1000 * Math.random()),
])
.then(function( res ){
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})
})();
此模式采用一组输入并使用 array.map() 取回一组已启动的承诺,这些承诺将按上述方式进行并行处理。注意这里没有使用 async/await。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(() => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf(),
vals = [
[1, 1000 * Math.random()],
[2, 1000 * Math.random()],
[3, 1000 * Math.random()],
[4, 1000 * Math.random()]
];
Promise.all( vals.map( v => { return randomResolve(v[0], v[1] ); } ) )
.then(function( res ){
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})
})();
此版本确实实现了 async/await。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(async () => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf(),
vals = [
[1, 1000 * Math.random()],
[2, 1000 * Math.random()],
[3, 1000 * Math.random()],
[4, 1000 * Math.random()]
];
let res = await Promise.all( vals.map( async v => { return await randomResolve( v[0], v[1] ); } ) );
// await the Promise.aall() call instead of using .then() afterwards with another closure then
// forEach v in vals, start and await a Promise from randomResolve() then return the result to map
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})();
我建议这样使用它:
const [
res1,
res2
] = await Promise.all([
asyncCall1(),
asyncCall1(),
]);
也许解决方案是使用 worker-farm,我不知道如何解释它,因为我是 NodeJS 的新手,但这里有一篇关于它的有趣文章:
在这种情况下,您可以通过以下方式使用 worker-farm
(我目前对这个主题没有很好的把握,请不要犹豫纠正我。)
首先,安装"worker-farm"
npm install --save worker-farm
然后:
// index.js
const workerFarm = require("worker-farm");
const service = workerFarm(require.resolve("./service"));
function Promise1() {
return new Promise(function(resolve, reject) {
service("promise1", (err, output) => {
resolve(true);
});
});
}
function Promise2() {
return new Promise(function(resolve, reject) {
service("promise2", (err, output) => {
resolve(true);
});
});
}
Promise.all([Promise1(), Promise2()]).then(function() {
console.log("All Done!");
});
在service.js
中创建一个需要大量执行时间的函数,执行完成后执行回调。
// service.js
const blocker = (input, callback) => {
// Number loop turns
// Adjust this number depending on your CPU
const nbTurn = 1000000000;
// How many log to display during the loop
const nbReminder = 4;
let i;
for (i = 0; i <= nbTurn; i++) {
const remainder = (i % Math.ceil(nbTurn / nbReminder)) / 100;
if (remainder === 0) {
console.log(input, i);
}
}
console.log(input + "end", i);
callback(null, nbTurn);
};
module.exports = blocker;
编辑:我找到了另一个解决方案:napajs (working example on codesandbox)
我相信 Promise.all 会并行执行您传递给它的所有函数,并且不关心返回的承诺完成的顺序。
但是当我写这段测试代码时:
function Promise1(){
return new Promise(function(resolve, reject){
for(let i = 0; i < 10; i++){
console.log("Done Err!");
}
resolve(true)
})
}
function Promise2(){
return new Promise(function(resolve, reject){
for(let i = 0; i < 10; i++){
console.log("Done True!");
}
resolve(true)
})
}
Promise.all([
Promise1(),
Promise2()
])
.then(function(){
console.log("All Done!")
})
我得到的结果是这样的
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done Err!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done True!
Done!
但是如果它们是 运行 并行的,我不希望它们同时执行并给我这样的结果吗?
Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Done Err!
Done True!
Etc. Etc.?
或者我在做这件事的过程中遗漏了什么?
那是因为你的 Promise 是阻塞同步的!尝试使用超时而不是同步循环:
function randomResolve(name) {
return new Promise(resolve => setTimeout(() => {
console.log(name);
resolve();
}, 100 * Math.random()));
}
Promise.all([
randomResolve(1),
randomResolve(2),
randomResolve(3),
randomResolve(4),
])
.then(function(){
console.log("All Done!")
})
A non-Async body 串行执行 当您在 body 中进行异步调用时(例如,点击 URL),数组中的其他 Promise 将开始执行。
在 Johannes Merz 开始的基础上,我提出这段代码来阐明事情是并行发生的。
JS 是单线程的,但是 Node.js 有很多工具可以显式和隐式地启动额外的线程。 Promises 公开了我们经常需要的更多功能,而无需显式启动新线程或进程。 Promise.all() 就是这样一个例子,但是您需要熟悉 Promises 才能使用它而不会给自己带来严重的麻烦,例如 Promise Scope 内存泄漏。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(() => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf();
Promise.all([
randomResolve(1, 1000 * Math.random()),
randomResolve(2, 1000 * Math.random()),
randomResolve(3, 1000 * Math.random()),
randomResolve(4, 1000 * Math.random()),
])
.then(function( res ){
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})
})();
此模式采用一组输入并使用 array.map() 取回一组已启动的承诺,这些承诺将按上述方式进行并行处理。注意这里没有使用 async/await。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(() => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf(),
vals = [
[1, 1000 * Math.random()],
[2, 1000 * Math.random()],
[3, 1000 * Math.random()],
[4, 1000 * Math.random()]
];
Promise.all( vals.map( v => { return randomResolve(v[0], v[1] ); } ) )
.then(function( res ){
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})
})();
此版本确实实现了 async/await。
function randomResolve(name,t) {
return new Promise(resolve => setTimeout(() => {
console.log({ name, t });
resolve({ name, t });
}, t));
}
(async () => {
// Get epoch time before starting so we can confirm the execution time reflects our slowest timeout
let start = new Date().valueOf(),
vals = [
[1, 1000 * Math.random()],
[2, 1000 * Math.random()],
[3, 1000 * Math.random()],
[4, 1000 * Math.random()]
];
let res = await Promise.all( vals.map( async v => { return await randomResolve( v[0], v[1] ); } ) );
// await the Promise.aall() call instead of using .then() afterwards with another closure then
// forEach v in vals, start and await a Promise from randomResolve() then return the result to map
console.info( res );
console.log("All Done!", parseInt(new Date().valueOf() - start) );
})();
我建议这样使用它:
const [
res1,
res2
] = await Promise.all([
asyncCall1(),
asyncCall1(),
]);
也许解决方案是使用 worker-farm,我不知道如何解释它,因为我是 NodeJS 的新手,但这里有一篇关于它的有趣文章:
在这种情况下,您可以通过以下方式使用 worker-farm
(我目前对这个主题没有很好的把握,请不要犹豫纠正我。)
首先,安装"worker-farm"
npm install --save worker-farm
然后:
// index.js
const workerFarm = require("worker-farm");
const service = workerFarm(require.resolve("./service"));
function Promise1() {
return new Promise(function(resolve, reject) {
service("promise1", (err, output) => {
resolve(true);
});
});
}
function Promise2() {
return new Promise(function(resolve, reject) {
service("promise2", (err, output) => {
resolve(true);
});
});
}
Promise.all([Promise1(), Promise2()]).then(function() {
console.log("All Done!");
});
在service.js
中创建一个需要大量执行时间的函数,执行完成后执行回调。
// service.js
const blocker = (input, callback) => {
// Number loop turns
// Adjust this number depending on your CPU
const nbTurn = 1000000000;
// How many log to display during the loop
const nbReminder = 4;
let i;
for (i = 0; i <= nbTurn; i++) {
const remainder = (i % Math.ceil(nbTurn / nbReminder)) / 100;
if (remainder === 0) {
console.log(input, i);
}
}
console.log(input + "end", i);
callback(null, nbTurn);
};
module.exports = blocker;
编辑:我找到了另一个解决方案:napajs (working example on codesandbox)