JavaScript,Node.js:Array.forEach是异步的吗?

R. *_*Gr. 358 javascript arrays foreach asynchronous node.js

我有一个关于Array.forEachJavaScript 的本机实现的问题:它是否异步?例如,如果我打电话:

[many many elements].forEach(function () {lots of work to do})
Run Code Online (Sandbox Code Playgroud)

这会不会阻塞吗?

Fel*_*ing 374

不,它是封锁的.看看算法规格.

但是,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);
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

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

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

    setTimeout(function() {
        process(todo.shift());
        if(todo.length > 0) {
            setTimeout(arguments.callee, 25);
        }
    }, 25);
}
Run Code Online (Sandbox Code Playgroud)

然后用以下方法调用它:

processArray([many many elements], function () {lots of work to do});
Run Code Online (Sandbox Code Playgroud)

那将是非阻塞的.该示例来自高性能JavaScript.

另一种选择可能是网络工作者.

  • 如果你正在使用Node.js,也可以考虑使用[process.nextTick](http://nodejs.org/docs/v0.4.0/api/process.html#process.nextTick)而不是setTimeout (35认同)
  • 从技术上讲,forEach不会"阻塞",因为CPU永远不会进入睡眠状态.它是同步的并且受CPU限制,当您希望节点应用程序响应事件时,它会感觉像"阻塞". (25认同)
  • 我相信这个答案,但在某些情况下似乎是错误的.`forEach`确实**不会阻止`await`语句,你应该使用`for`循环:http://stackoverflow.com/questions/37962880/why-does-await-not-wait-和为什么此结果sequelize - 既不后回应,也没有错误?noredirect = 1#comment63373960_37962880 (4认同)
  • [async](https://www.npmjs.org/package/async)可能是一个更合适的解决方案(实际上只是看到有人发布了这个答案!). (3认同)
  • @Richard:当然.你只能在`async`函数中使用`await`.但是`forEach`并不知道异步函数是什么.请记住,异步函数只是返回promise的函数.你期望`forEach`能够处理从回调中返回的承诺吗?`forEach`完全忽略回调的返回值.如果它本身是异步的,它只能处理异步回调. (3认同)

Cao*_*lan 75

如果你需要一个异步友好版本的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
});
Run Code Online (Sandbox Code Playgroud)

  • 如果您需要确保 ** 一次只为一项(按集合顺序)** 运行异步操作,则必须改用 `eachSeries`。 (3认同)

Dav*_*son 15

在Node中进行非常繁重的计算有一个共同的模式可能适用于你......

Node是单线程的(作为一个深思熟虑的设计选择,参见什么是Node.js?); 这意味着它只能使用单个核心.现代的盒子有8个,16个甚至更多的核心,因此可以使90%以上的机器闲置.REST服务的常见模式是为每个核心启动一个节点进程,并将它们放在像http://nginx.org/这样的本地负载均衡器之后.

分娩孩子 - 对于你想要做的事情,还有另一种常见的模式,就是要求儿童过程完成繁重的工作.好处是子进程可以在后台执行大量计算,而父进程可以响应其他事件.问题是你不能/不应该与这个子进程共享内存(不是没有很多扭曲和一些本机代码); 你必须传递消息.如果输入和输出数据的大小与必须执行的计算相比较小,则可以很好地工作.您甚至可以启动子node.js进程并使用您之前使用的相同代码.

例如:

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

  • 为了清楚起见...... Node不是单线程的,但是JavaScript的执行是.IO和什么不在不同的线程上运行. (10认同)
  • 正确,我只是澄清,因为这个概念让很多人困惑. (4认同)
  • @Brad - 也许吧.这是依赖于实现的.通过适当的内核支持,Node和内核之间的接口可以基于事件 - kqueue(mac),epoll(linux),IO完成端口(windows).作为后备,线程池也可以工作.你的基本观点是正确的.低级Node实现可能有多个线程.但是他们永远不会直接将它们暴露给JS用户区,因为这会破坏整个语言模型. (3认同)

Tob*_*obu 5

Array.forEach意味着不需要等待就可以进行计算,并且在事件循环中使计算异步化是没有任何收获的(如果需要多核计算,网络工作人员可以添加多处理功能)。如果要等待多个任务结束,请使用计数器,您可以将其包装在信号量类中。


Jos*_* Mc 5

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

这正是我对 es7 感到兴奋的原因,将来您将能够执行类似以下代码的操作(某些规范不完整,因此请谨慎使用,我会尽量保持最新)。但是基本上使用新的 :: bind 运算符,您将能够在对象上运行方法,就好像对象的原型包含该方法一样。例如 [Object]::[Method] 通常你会调用 [Object].[ObjectsMethod]

请注意今天(16 年 7 月 24 日)执行此操作并使其在所有浏览器中运行,您需要为以下功能转换代码:导入/导出箭头函数承诺异步/等待和最重要的函数绑定。如果需要,可以修改下面的代码以仅使用函数绑定,所有这些功能今天都可以使用babel巧妙地使用。

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

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

await [many many elements]::asyncForEach(() => lots of work to do);
Run Code Online (Sandbox Code Playgroud)

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;
    });
};
Run Code Online (Sandbox Code Playgroud)