"Yield" Node.js 中的计算
"Yield" a computation in Node.js
我的节点应用程序中有更大的计算量(~0.5 秒),我想在不使用 webworker 的情况下使其成为非阻塞的。 (我认为网络工作者在这种情况下会有点矫枉过正)
有没有办法强制 return 进入主循环,以便让节点有机会处理另一个请求?
听起来你是说你想在主线程上以一口大小的块进行计算,而不是将其旋转到它自己的线程(例如,使用 webworker-threads 或子进程等等)。
我能想到三个方案:
ES6 生成器函数。
一个 return 设置其状态并允许您使用状态对象再次调用它的函数(这基本上就是 ES6 生成器函数,但它们为您提供了更好的语法)。
通过使用 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
})();
我的节点应用程序中有更大的计算量(~0.5 秒),我想在不使用 webworker 的情况下使其成为非阻塞的。 (我认为网络工作者在这种情况下会有点矫枉过正)
有没有办法强制 return 进入主循环,以便让节点有机会处理另一个请求?
听起来你是说你想在主线程上以一口大小的块进行计算,而不是将其旋转到它自己的线程(例如,使用 webworker-threads 或子进程等等)。
我能想到三个方案:
ES6 生成器函数。
一个 return 设置其状态并允许您使用状态对象再次调用它的函数(这基本上就是 ES6 生成器函数,但它们为您提供了更好的语法)。
通过使用
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
})();