我有一个关于原生数组的问题。JavaScript的每一个实现都是异步的吗? 例如,如果我调用:

[many many elements].forEach(function () {lots of work to do})

这将是非阻塞的吗?


当前回答

下面是一个小例子,你可以运行来测试它:

[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);
});

它将生成如下内容(如果花费的时间太少/太多,可以增加/减少迭代次数):

(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

其他回答

不,它堵塞了。看一下算法的说明。

然而,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);
    }
  };
}

如果你必须为每个元素执行大量代码,你应该考虑使用不同的方法:

function processArray(items, process) {
    var todo = items.concat();

    setTimeout(function() {
        process(todo.shift());
        if(todo.length > 0) {
            setTimeout(arguments.callee, 25);
        }
    }, 25);
}

然后用:

processArray([many many elements], function () {lots of work to do});

这将是非阻塞的。这个例子来自于高性能JavaScript。

另一个选择可能是网络工作者。

这些代码片段将使您更好地理解forEach和forOf的比较。

/* eslint-disable no-console */ async function forEachTest() { console.log('########### Testing forEach ################ ') console.log('start of forEachTest func') let a = [1, 2, 3] await a.forEach(async (v) => { console.log('start of forEach: ', v) await new Promise(resolve => setTimeout(resolve, v * 1000)) console.log('end of forEach: ', v) }) console.log('end of forEachTest func') } forEachTest() async function forOfTest() { await new Promise(resolve => setTimeout(resolve, 10000)) //just see console in proper way console.log('\n\n########### Testing forOf ################ ') console.log('start of forOfTest func') let a = [1, 2, 3] for (const v of a) { console.log('start of forOf: ', v) await new Promise(resolve => setTimeout(resolve, v * 1000)) console.log('end of forOf: ', v) } console.log('end of forOfTest func') } forOfTest()

数组中。forEach是用来计算东西而不是等待的,并且在事件循环中使计算异步是没有任何收获的(如果你需要多核计算,web工作者会添加多处理)。如果希望等待多个任务结束,可以使用计数器,可以将计数器包装在信号量类中。

这是一个不需要第三方库就可以使用的简短异步函数

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);
};

使用的承诺。每个蓝鸟图书馆。

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 iterator function with the signature (value, index, length) where the value is 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.

如果所有的迭代都成功解决了,请保证。每一个都解析为原始数组。但是,如果一次迭代失败或出错,请保证。每一个都立即停止执行,并且不再处理任何进一步的迭代。在这种情况下,返回的是错误或被拒绝的值,而不是原始数组。

这种方法是用来消除副作用的。

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");
});