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

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

这将是非阻塞的吗?


当前回答

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

然而,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。

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

其他回答

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

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

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

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

编辑2018-10-11: 看起来下面描述的标准很有可能无法通过,可以考虑管道作为替代方案(虽然行为不完全相同,但方法可以在类似的庄园中实现)。

这就是为什么我对es7感到兴奋,在未来你将能够做一些像下面的代码(一些规格不完整,所以使用时要小心,我会尽量保持最新)。但是基本上使用新的::bind操作符,你将能够在对象上运行一个方法,就好像对象的原型包含了这个方法一样。例如[Object]::[Method],通常你会调用[Object].[ObjectsMethod]

注意今天(7月24日-16日)要做到这一点,并让它在所有浏览器中都能工作,你需要编译以下功能的代码:导入/导出,箭头函数,承诺,Async / Await,以及最重要的函数绑定。如果必要,下面的代码可以修改为只使用函数绑定,所有这些功能今天都可以使用babel。

YourCode.js(其中“许多工作要做”必须简单地返回一个承诺,在异步工作完成时解决它。)

import { asyncForEach } from './ArrayExtensions.js';

await [many many elements]::asyncForEach(() => lots of work to do);

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

如果您需要一个异步友好的Array版本。对于ach和类似的,它们在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
});

虽然数组。forEach不是异步的,你可以得到异步的“最终结果”。在下面的例子:

delayFunction(x) { 归还新承诺( (resolve) => setTimeout(() => resolve(x), 1000) ); } [1,2,3].forEach(async(x) => { console.log (x); console.log(等待delayFunction (x)); });