问题 如何防止NodeJS事件循环退出?


我已经实现了一个需要回调的本机函数。 NodeJS知道接口,但它对它的实现一无所知。此本机函数接收回调,并在结果准备好时调用它。我不希望在没有调用回调的情况下退出事件循环。

这是 这种问题的一个例子

目前我需要做一些I / O(即使它是一个愚蠢的超时)来强制NodeJS等待我的功能。

Boost.Asio我只是实例化一个 work 对象并在调用回调时销毁它。当这个对象被保留时,Boost.Asio的事件循环不会退出。 NodeJS有类似的方法吗?我在NodeJS中使用什么(如果您的答案没有提到计时器,奖金)?


5954
now


起源

你的问题还不清楚。你是什​​么意思Node对此一无所知?那你怎么称呼它?这是一个原生模块吗? - Alexander O'Mara
你能否展示你到目前为止尝试过的代码 - teivaz
显示您尝试过的代码 - Nazmul Hossain
大家好,我已经更新了这个问题。抱歉让它“直接”/“准客观”,但我有很长的问题经验(根据我的经验,人们逃避他们,甚至没有尝试)。 - vinipsmaker
@vinipsmaker这是一个公平的观点。但是如果没有更多的解释,我们无法帮助你。你必须尽可能清楚。尝试使用降价功能(标题,重点,......)。 - Pierre C.


答案:


最好的方法是写一个 C ++插件 和使用       手柄 由libuv提供(当然,符合您要求的那个 - 见官方 文件 有关详细信息)。

如果您不想这样做或者您不能这样做(就是这种情况,如果我正确理解了这个问题),那么其他答案中没有提到的可行解决方案就是使用 process.nextTick 安排一个函数,检查每个滴答是否循环可以到期。
看到 这里 有关的更多详细信息 process.nextTick

作为一个最小的,工作, 没完没了 例:

var process = require('process')
var stop = false;
var f = function() { if(!stop) process.nextTick(f) }
f()

这样你的功能就是负责设置 stop 控制变量一旦完成执行,循环就会停止。

如果您有多个要等待的回调,只需使用 计数器 并检查它寻找0。
如果您不想在每次添加新函数时显式设置和更新计数器的值(容易出错),您可以轻松编写 发射台 开始增加计数器的功能,并在需要时安排检查下一个计时器。
您还可以将回调作为额外参数传递给函数,以便在它们结束时进行通知,以便它们不必明确处理计数器本身。

使用在下一个计时器上安排的专用功能的一个优点是读者清楚你正在做什么。
另一方面,假的服务器,未来安排的超时或恢复并且从未使用过的I / O流非常模糊,因为读者不知道你为什么这么做。


4
2017-08-31 23:26



我看一下你提到的libuv所提供的句柄,所有这些句柄都很重(每次迭代都会调用函数),NodeJS已经将它们提供给JavaScript代码(即不需要使用C ++本地化)。我认为通过使用像您提议的计数器而不是多次可以降低成本 bool 变量和多个句柄,但仍然...我将保持超时方法(毫秒是CPU周期的年龄)。我选择了你的答案,因为你似乎是那个走得更远的人。它让我相信所有的选择都被探索过。谢谢。 - vinipsmaker
@vinipsmaker嗯,你正在使用nodejs,这是一个libuv和v8层,让你使用JavaScript服务器端。指出一个 if 成本太高听起来像是过早的优化。无论如何,我很高兴我帮助过你。 :-) - skypjack
@vinipsmaker我没有使用nextTick方法的原因是f()成为一个紧凑的循环并将消耗整个cpu核心。 - John Siu
这是一个非常易读的解决方案,可以写成: (function endlessProcess() { process.nextTick(endlessProcess) })() 但不像 setTimeout/setInterval 功能,它利用所有可用的CPU,这是不推荐的。 - MyUserInStackOverflow


方法1:创建虚拟服务器(为多个回调更新):

var counter = 0;

var server = require('net').createServer().listen(); // <-- Dummy server
console.log('Dummy server start.');

var ffi = require('ffi'),
    ref = require('ref')

var lib = ffi.Library('./libffi_async_demo', {
  'print_thread_id': [ 'void', [] ],
  'run_delayed': [ 'void', [ 'pointer' ] ],
});

var checkExit = function (){
  counter--;
  if (counter===0) {
    server.close(); // <-- exit Dummy Server
    console.log('Dummy server stop.');
  }
}

// Wrapper for lib.run_delay()
run_delay = function(cb) {
  counter++; // <-- increase counter
  lib.run_delayed(cb);
}

var callback1 = ffi.Callback('void', [], function() {
  console.log("js callback1 started");
  lib.print_thread_id();
  console.log("js callback1 finished");

  checkExit(); // <-- call at the end of each callback
})

var callback2 = ffi.Callback('void', [], function() {
  console.log("js callback2 started");
  lib.print_thread_id();
  console.log("js callback2 finished");

  checkExit(); // <-- call at the end of each callback
})

var callback3 = ffi.Callback('void', [], function() {
  console.log("js callback3 started");
  lib.print_thread_id();
  console.log("js callback3 finished");

  checkExit(); // <-- call at the end of each callback
})

run_delay(callback1); // use wrapper
run_delay(callback2); // use wrapper
run_delay(callback3); // use wrapper

方法2:长超时,回调结