我已经实现了一个需要回调的本机函数。 NodeJS知道接口,但它对它的实现一无所知。此本机函数接收回调,并在结果准备好时调用它。我不希望在没有调用回调的情况下退出事件循环。
这是an example of such a problem。
目前我需要做一些I / O(即使这是一个愚蠢的超时)来强制NodeJS等待我的功能。
在Boost.Asio I'd just instantiate a work
object and destroy it when the callback is called。当这个对象被保留时,Boost.Asio的事件循环不会退出。 NodeJS有类似的方法吗?我在NodeJS中使用什么(如果您的答案没有提到计时器,可以获得奖金)?
答案 0 :(得分:5)
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
var timeout; // Hold timeout reference from setTimeout()
var ffi = require('ffi'),
ref = require('ref')
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var callback = ffi.Callback('void', [], function() {
console.log("js callback started");
lib.print_thread_id()
console.log("js callback finished");
// Use one of the following 3:
//timeout.unref(); // <-- remove timer from Node event loop
//require('process').exit(); //<-- end process
clearTimeout(timeout); // <-- cancel timer
})
lib.run_delayed(callback)
timeout = setTimeout(function() { }, 3600000); // <-- reasonably long timeout, eg. 1hr
答案 1 :(得分:5)
最好的方法是撰写C++ addon并使用libuv提供的one of the handles(当然,那是符合您的要求 - 有关详细信息,请参阅官方documentation。
如果您不想这样做或者您不能这样做(就是这样,如果我已经正确地理解了这个问题),那么另一个没有提到的可行解决方案答案是使用process.nextTick
来安排一个函数,该函数在每个tick处检查循环是否可以到期
有关process.nextTick
。
作为一个最小的,有效的永无止境的示例:
var process = require('process')
var stop = false;
var f = function() { if(!stop) process.nextTick(f) }
f()
这样你的函数在完成执行后负责设置stop
控制变量,然后循环就会停止。
如果您有多个要等待的回调,只需使用计数器并检查它是否正在寻找0。 如果您不希望每次添加新功能时都明确设置和更新计数器的值(容易出错),您可以轻松编写启动器来启动增加的功能如果需要,计数器并安排检查下一个刻度 您还可以将回调作为额外参数传递给函数,以便在它们结束时通知它们,这样它们就不会明确地处理计数器本身。
使用在下一个刻度盘上安排的专用功能的一个优点是读者清楚你正在做什么。
另一方面,假冒服务器,未来安排的超时或恢复并且从未使用过的I / O流都非常模糊,因为读者并不知道你为什么这么做。
答案 2 :(得分:2)
您还可以使用stdin
可读流来保持退出循环。
const callback = ffi.Callback('void', [], function() {
// do your stuff here
// switch stream out of flowing mode.
process.stdin.pause();
});
// set stream into flowing mode ("old mode")
process.stdin.resume();
lib.run_delayed(callback);
参考:https://nodejs.org/api/process.html#process_process_stdin
中的注意答案 3 :(得分:2)
在代码中的某个位置启动repl,它会阻止您的应用退出。
const repl = require('repl');
repl.start('> ')
完成后,请致电
process.exit()
答案 4 :(得分:1)
创建大超时 - 它会阻止节点退出并防止您无限期地等待(节点外部)结果:
var ffi = require('ffi'),
ref = require('ref')
var ffiTimeout;
var lib = ffi.Library('./libffi_async_demo', {
'print_thread_id': [ 'void', [] ],
'run_delayed': [ 'void', [ 'pointer' ] ],
});
var ffiDidTimedOut = false;
var cancelFfi = function(timeoutCb) {
return function() {
ffiDidTimedOut = true;
timeoutCb();
}
}
var callback = ffi.Callback('void', [], function() {
if (ffiDidTimedOut) {
return; // sorry, we waited too long and already started doing something else
}
// all good, async ffi finished within expected time and we are back in our js land
clearTimeout(ffiTimeout);
lib.print_thread_id()
})
lib.run_delayed(callback)
// continueIfCancelledCallback is your continuation "what to do id ffi actually takes more than 20 seconds to run"
ffiTimeout = setTimeout(cancelFfi(continueIfCancelledCallback), 20000); // 20 seconds