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

IT技术 javascript arrays asynchronous foreach node.js
2021-01-20 13:55:57

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

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

这会是非阻塞的吗?

6个回答

不,它在阻塞。看看算法规范

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

这将是非阻塞的。该示例取自High Performance JavaScript

另一种选择可能是web workers

我相信这个答案,但在某些情况下似乎是错误的。forEach没有对阻止await比如语句,你应该宁愿使用一个for循环:stackoverflow.com/questions/37962880/...
2021-03-19 13:55:57
async在这里可能是更合适的解决方案(实际上只是看到有人将其发布为答案!)。
2021-03-22 13:55:57
从技术上讲,forEach 不会“阻塞”,因为 CPU 永远不会进入睡眠状态。它是同步的且受 CPU 限制,当您期望节点应用程序响应事件时,这感觉就像“阻塞”。
2021-03-27 13:55:57
@理查德:当然。您只能使用await内部async函数。forEach不知道异步函数是什么。请记住,异步函数只是返回Promise的函数。您是否希望forEach处理从回调返回的Promise?forEach完全忽略回调的返回值。如果它本身是异步的,它只能处理异步回调。
2021-03-29 13:55:57
如果您使用的是 Node.js,还可以考虑使用process.nextTick而不是 setTimeout
2021-04-02 13:55:57

如果你需要一个异步友好的Array.forEach和类似的版本,它们可以在 Node.js 的“async”module中找到:http : //github.com/caolan/async ...作为奖励,这个module也可以在浏览器中使用.

async.each(openFiles, saveFile, function(err){
    // if any of the saves produced an error, err would equal that error
});
@JohnKennedy 我以前见过你!
2021-03-19 13:55:57
如果您需要确保 async opeartion 一次只为一项(按集合顺序)运行,则必须eachSeries改用。
2021-03-27 13:55:57

在 Node 中进行非常繁重的计算有一种常见模式可能适用于您......

Node 是单线程的(作为一个深思熟虑的设计选择,请参阅什么是 Node.js?);这意味着它只能使用一个内核。现代机器有 8 个、16 个甚至更多的内核,因此这可能会使 90+% 的机器处于空闲状态。REST 服务的常见模式是为每个核心启动一个节点进程,并将这些进程置于本地负载均衡器(如http://nginx.org/ )之后

派生一个子-对于你正在尝试做的,还有另外一个常见的模式,分叉关子进程做繁重。好处是子进程可以在后台进行大量计算,而您的父进程响应其他事件。问题是你不能/不应该与这个子进程共享内存(不是没有很多扭曲和一些本机代码);你必须传递消息。如果输入和输出数据的大小与必须执行的计算相比很小,这将非常有效。您甚至可以启动子 node.js 进程并使用您之前使用的相同代码。

例如:

var child_process = require('child_process');
函数 run_in_child(array, cb) {
    var process = child_process.exec('node libfn.js', function(err, stdout, stderr) {
        var 输出 = JSON.parse(stdout);
        cb(错误,输出);
    });
    process.stdin.write(JSON.stringify(array), 'utf8');
    process.stdin.end();
}
@布拉德 - 也许。这取决于实现。通过适当的内核支持,Node 和内核之间的接口可以是基于事件的——kqueue (mac)、epoll (linux)、IO 完成端口 (windows)。作为后备,线程池也可以使用。不过你的基本观点是对的。低级 Node 实现可能有多个线程。但是他们永远不会将它们直接暴露给 JS 用户空间,因为这会破坏整个语言模型。
2021-03-22 13:55:57
只是要清楚...... Node 不是单线程的,但你的 JavaScript 的执行是。IO 以及不在单独线程上运行的内容。
2021-04-04 13:55:57
正确,我只是澄清一下,因为这个概念混淆了很多人。
2021-04-06 13:55:57

Array.forEach用于计算不等待的东西,并且在事件循环中进行异步计算没有任何好处(如果您需要多核计算,网络工作者会添加多处理)。如果您想等待多个任务结束,请使用计数器,您可以将其包装在信号量类中。

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

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

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

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

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