"Yield" Node.js 中的计算

"Yield" a computation in Node.js

我的节点应用程序中有更大的计算量(~0.5 秒),我想在不使用 webworker 的情况下使其成为非阻塞的。 (我认为网络工作者在这种情况下会有点矫枉过正)
有没有办法强制 return 进入主循环,以便让节点有机会处理另一个请求?

听起来你是说你想在主线程上以一口大小的块进行计算,而不是将其旋转到它自己的线程(例如,使用 webworker-threads 或子进程等等)。

我能想到三个方案:

  1. ES6 生成器函数。

  2. 一个 return 设置其状态并允许您使用状态对象再次调用它的函数(这基本上就是 ES6 生成器函数,但它们为您提供了更好的语法)。

  3. 通过使用 nextTick 自行继续 运行 的函数,您无法控制它的运行方式。

第三个选项提供调用代码最少的控制;第一个和第三个可能最容易实现。

ES6 生成器函数

您可以通过 --harmony_generators 标志在最新版本的 NodeJS 中使用 ES6 的 生成器函数 。生成器函数可以 "yield" 返回到调用代码,然后调用代码可以告诉它们稍后从中断的地方继续。

这里有一个简单的生成器的例子,它计数到一个限制,每次调用它时计数加一:

function* counter(start, inc, max) {
    while (start < max) {
        yield start;
        start += inc;
    }
}

var x = counter(1, 1, 10);
console.log(x.next().value); // 1
console.log(x.next().value); // 2
console.log(x.next().value); // 3

请注意,这 不会 将计算分流到另一个线程,它只是让您做一点,做点别的,然后回来做一点更多。

一个return发送并接受其状态的函数

如果你不能使用生成器,你可以实现同样的事情,让你的所有局部变量成为一个对象的属性,拥有那个对象的函数return,然后让它再次接受它作为参数:

function counter(start, inc, max) {
    var state;
    if (typeof start === "object") {
        state = start;
        if (!state.hasOwnProperty("value")) {
            state.value = state.start;
        } else if (state.value < state.max) {
            state.value += state.inc;
        } else {
            state.done = true;
        }
    } else {
        state = {
            start: start,
            inc: inc,
            max: max,
            done: false
        };
    }
    return state;
}

var x = counter(1, 1, 10);
console.log(counter(x).value); // 1
console.log(counter(x).value); // 2
console.log(counter(x).value); // 3

您可以看到生成器如何稍微简化事情。

在没有调用代码控制的情况下运行的函数

这是一个函数示例,它使用 nextTick 以小块的形式执行其任务,并在完成时通知您:

function counter(start, inc, max, callback) {
    go();

    function go() {
        var done = start >= max;
        callback(start, done);
        if (!done) {
            ++start;
            process.nextTick(go);
        }
    }
}

counter(1, 1, 10, function(value, done) {
    console.log(value);
});

现在,您不能在全局范围内使用生成器(您可能根本不想这样做),您必须在严格模式下的函数中使用它。 (即使是全局 "use strict" 也不会这样做。)这是因为 V8 仍在获取其 ES6 功能...

当前版本节点的完整示例脚本(允许上述情况):

"use strict";
(function() {
    function* counter(start, inc, max) {
        while (start < max) {
            yield start;
            start += inc;
        }
    }

    var x = counter(1, 1, 10);
    console.log(x.next().value); // 1
    console.log(x.next().value); // 2
    console.log(x.next().value); // 3
})();