JavaScript、Node.js:Array.forEach 是异步的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5050265/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript, Node.js: is Array.forEach asynchronous?
提问by R. Gr.
I have a question regarding the native Array.forEach
implementation of JavaScript: Does it behave asynchronously?
For example, if I call:
我有一个关于Array.forEach
JavaScript的本机实现的问题:它是否异步运行?例如,如果我打电话:
[many many elements].forEach(function () {lots of work to do})
Will this be non-blocking?
这会是非阻塞的吗?
回答by Felix Kling
No, it is blocking. Have a look at the specification of the algorithm.
However a maybe easier to understand implementation is given on MDN:
然而,在MDN上给出了一个可能更容易理解的实现:
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp */)
{
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in t)
fun.call(thisp, t[i], i, t);
}
};
}
If you have to execute a lot of code for each element, you should consider to use a different approach:
如果必须为每个元素执行大量代码,则应考虑使用不同的方法:
function processArray(items, process) {
var todo = items.concat();
setTimeout(function() {
process(todo.shift());
if(todo.length > 0) {
setTimeout(arguments.callee, 25);
}
}, 25);
}
and then call it with:
然后调用它:
processArray([many many elements], function () {lots of work to do});
This would be non-blocking then. The example is taken from High Performance JavaScript.
这将是非阻塞的。该示例取自High Performance JavaScript。
Another option might be web workers.
另一种选择可能是web workers。
回答by Caolan
If you need an asynchronous-friendly version of Array.forEach
and similar, they're available in the Node.js 'async' module: http://github.com/caolan/async...as a bonus this module also works in the browser.
如果你需要一个Array.forEach
类似的异步友好版本,它们可以在 Node.js 的“async”模块中找到:http: //github.com/caolan/async...作为奖励,这个模块也可以在浏览器中使用.
async.each(openFiles, saveFile, function(err){
// if any of the saves produced an error, err would equal that error
});
回答by Dave Dopson
There is a common pattern for doing a really heavy computation in Node that may be applicable to you...
在 Node 中进行非常繁重的计算有一种常见模式可能适用于您......
Node is single-threaded (as a deliberate design choice, see What is Node.js?); this means that it can only utilize a single core. Modern boxes have 8, 16, or even more cores, so this could leave 90+% of the machine idle. The common pattern for a REST service is to fire up one node process per core, and put these behind a local load balancer like http://nginx.org/.
Node 是单线程的(作为一个深思熟虑的设计选择,请参阅什么是 Node.js?);这意味着它只能使用一个内核。现代机器有 8 个、16 个甚至更多的内核,因此这可能会使 90+% 的机器处于空闲状态。REST 服务的常见模式是为每个核心启动一个节点进程,并将这些进程置于本地负载均衡器(如http://nginx.org/)之后。
Forking a child- For what you are trying to do, there is another common pattern, forking off a child process to do the heavy lifting. The upside is that the child process can do heavy computation in the background while your parent process is responsive to other events. The catch is that you can't / shouldn't share memory with this child process (not without a LOT of contortions and some native code); you have to pass messages. This will work beautifully if the size of your input and output data is small compared to the computation that must be performed. You can even fire up a child node.js process and use the same code you were using previously.
派生一个子-对于你正在尝试做的,还有另外一个常见的模式,分叉关子进程做繁重。好处是子进程可以在后台进行大量计算,而您的父进程响应其他事件。问题是你不能/不应该与这个子进程共享内存(不是没有很多扭曲和一些本机代码);你必须传递消息。如果输入和输出数据的大小与必须执行的计算相比很小,这将非常有效。您甚至可以启动子 node.js 进程并使用您之前使用的相同代码。
For example:
例如:
var child_process = require('child_process'); function run_in_child(array, cb) { var process = child_process.exec('node libfn.js', function(err, stdout, stderr) { var output = JSON.parse(stdout); cb(err, output); }); process.stdin.write(JSON.stringify(array), 'utf8'); process.stdin.end(); }
回答by Tobu
Array.forEach
is meant for computing stuff not waiting, and there is nothing to be gained making computations asynchronous in an event loop (webworkers add multiprocessing, if you need multi-core computation). If you want to wait for multiple tasks to end, use a counter, which you can wrap in a semaphore class.
Array.forEach
用于计算不等待的东西,并且在事件循环中进行异步计算没有任何好处(如果您需要多核计算,网络工作者会添加多处理)。如果您想等待多个任务结束,请使用计数器,您可以将其包装在信号量类中。
回答by Josh Mc
Edit 2018-10-11: It looks like there is a good chance the standard described below may not go through, consider pipelineingas an alternative (does not behave exactly the same but methods could be implemented in a similar manor).
编辑 2018-10-11:看起来下面描述的标准很有可能无法通过,考虑将流水线作为替代方案(行为不完全相同,但方法可以在类似的庄园中实现)。
This is exactly why I am excited about es7, in future you will be able to do something like the code below (some of the specs are not complete so use with caution, I will try to keep this up to date). But basically using the new :: bind operator, you will be able to run a method on an object as if the object's prototype contains the method. eg [Object]::[Method] where normally you would call [Object].[ObjectsMethod]
这正是我对 es7 感到兴奋的原因,将来您将能够执行类似以下代码的操作(某些规范不完整,因此请谨慎使用,我会尽量保持最新状态)。但是基本上使用新的 :: bind 运算符,您将能够在对象上运行方法,就好像对象的原型包含该方法一样。例如 [Object]::[Method] 通常你会调用 [Object].[ObjectsMethod]
Note to do this today (24-July-16) and have it work in all browsers you will need to transpile your code for the following functionality:Import / Export, Arrow functions, Promises, Async / Awaitand most importantly function bind. The code below could be modfied to use only function bind if nessesary, all this functionality is neatly available today by using babel.
请注意今天(16 年 7 月 24 日)执行此操作并使其在所有浏览器中运行,您需要为以下功能转译代码:导入/导出、箭头函数、承诺、异步/等待和最重要的函数绑定。如果需要,可以修改下面的代码以仅使用函数绑定,所有这些功能今天都可以使用babel巧妙地使用。
YourCode.js (where 'lots of work to do' must simply return a promise, resolving it when the asynchronous work is done.)
YourCode.js(其中“很多工作要做”必须简单地返回一个承诺,在异步工作完成时解决它。)
import { asyncForEach } from './ArrayExtensions.js';
await [many many elements]::asyncForEach(() => lots of work to do);
ArrayExtensions.js
ArrayExtensions.js
export function asyncForEach(callback)
{
return Promise.resolve(this).then(async (ar) =>
{
for(let i=0;i<ar.length;i++)
{
await callback.call(ar, ar[i], i, ar);
}
});
};
export function asyncMap(callback)
{
return Promise.resolve(this).then(async (ar) =>
{
const out = [];
for(let i=0;i<ar.length;i++)
{
out[i] = await callback.call(ar, ar[i], i, ar);
}
return out;
});
};
回答by Rax Wunter
This is a short asynchronous function to use without requiring third party libs
这是一个简短的异步函数,无需第三方库即可使用
Array.prototype.each = function (iterator, callback) {
var iterate = function () {
pointer++;
if (pointer >= this.length) {
callback();
return;
}
iterator.call(iterator, this[pointer], iterate, pointer);
}.bind(this),
pointer = -1;
iterate(this);
};
回答by Philip Kirkbride
There is a package on npm for easy asynchronous for each loops.
npm 上有一个包,可以轻松实现每个循环的异步。
var forEachAsync = require('futures').forEachAsync;
// waits for one request to finish before beginning the next
forEachAsync(['dogs', 'cats', 'octocats'], function (next, element, index, array) {
getPics(element, next);
// then after all of the elements have been handled
// the final callback fires to let you know it's all done
}).then(function () {
console.log('All requests have finished');
});
Also another variation forAllAsync
AllAsync 的另一个变体
回答by signo
It is possible to code even the solution like this for example :
甚至可以编写这样的解决方案,例如:
var loop = function(i, data, callback) {
if (i < data.length) {
//TODO("SELECT * FROM stackoverflowUsers;", function(res) {
//data[i].meta = res;
console.log(i, data[i].title);
return loop(i+1, data, errors, callback);
//});
} else {
return callback(data);
}
};
loop(0, [{"title": "hello"}, {"title": "world"}], function(data) {
console.log("DONE\n"+data);
});
On the other hand, it is much slower than a "for".
另一方面,它比“for”慢得多。
Otherwise, the excellent Async library can do this: https://caolan.github.io/async/docs.html#each
否则,优秀的 Async 库可以做到这一点:https: //caolan.github.io/async/docs.html#each
回答by adiian
Here is a small example you can run to test it:
这是一个小示例,您可以运行它来测试它:
[1,2,3,4,5,6,7,8,9].forEach(function(n){
var sum = 0;
console.log('Start for:' + n);
for (var i = 0; i < ( 10 - n) * 100000000; i++)
sum++;
console.log('Ended for:' + n, sum);
});
It will produce something like this(if it takes too less/much time, increase/decrease the number of iterations):
它会产生这样的东西(如果它花费的时间太少/太多,增加/减少迭代次数):
(index):48 Start for:1
(index):52 Ended for:1 900000000
(index):48 Start for:2
(index):52 Ended for:2 800000000
(index):48 Start for:3
(index):52 Ended for:3 700000000
(index):48 Start for:4
(index):52 Ended for:4 600000000
(index):48 Start for:5
(index):52 Ended for:5 500000000
(index):48 Start for:6
(index):52 Ended for:6 400000000
(index):48 Start for:7
(index):52 Ended for:7 300000000
(index):48 Start for:8
(index):52 Ended for:8 200000000
(index):48 Start for:9
(index):52 Ended for:9 100000000
(index):45 [Violation] 'load' handler took 7285ms
回答by Igor Litvinovich
Use Promise.eachof bluebirdlibrary.
使用Promise.each的bluebird库。
Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise
This method iterates over an array, or a promise of an array, which contains promises (or a mix of promises and values) with the given iteratorfunction with the signature (value, index, length)where the valueis the resolved value of a respective promise in the input array. Iteration happens serially.If the iterator function returns a promise or a thenable, then the result of the promise is awaited before continuing with next iteration. If any promise in the input array is rejected, then the returned promise is rejected as well.
此方法迭代数组或数组的承诺,其中包含承诺(或承诺和值的混合),具有给定的迭代器函数和签名(值,索引,长度),其中值是解析的值输入数组中的相应承诺。迭代是连续发生的。如果迭代器函数返回一个promise 或thenable,则在继续下一次迭代之前等待promise 的结果。如果输入数组中的任何承诺被拒绝,则返回的承诺也将被拒绝。
If all of the iterations resolve successfully, Promise.eachresolves to the original array unmodified. However, if one iteration rejects or errors, Promise.eachceases execution immediately and does not process any further iterations. The error or rejected value is returned in this case instead of the original array.
如果所有迭代都成功解析,Promise.each将解析为原始数组 unmodified。但是,如果一次迭代失败或出错,Promise.each 会立即停止执行并且不处理任何进一步的迭代。在这种情况下返回错误或拒绝值而不是原始数组。
This method is meant to be used for side effects.
此方法旨在用于治疗副作用。
var fileNames = ["1.txt", "2.txt", "3.txt"];
Promise.each(fileNames, function(fileName) {
return fs.readFileAsync(fileName).then(function(val){
// do stuff with 'val' here.
});
}).then(function() {
console.log("done");
});