如何防止Node.js在等待回调时退出?

时间:2011-06-22 15:43:37

标签: node.js

我有这样的代码:

var client = new mysql.Client(options);
console.log('Icanhasclient');

client.connect(function (err) {
  console.log('jannn');
  active_db = client;
  console.log(err);
  console.log('hest');

  if (callback) {
    if (err) {
      callback(err, null);
    }

    callback(null, active_db);
  }
});

我的问题是Node运行时会立即终止。它打印'Icanhasclient',但回调中没有调用console.log。

(此示例中的mysql为node-mysql

有没有什么可以让node.js在退出之前等待回调完成?

8 个答案:

答案 0 :(得分:47)

回调未排队

节点一直运行,直到所有事件队列为空。当诸如

之类的调用时,会将回调添加到事件队列
  emmiter1.on('this_event',callback).

已执行。此调用是模块开发人员编写的代码的一部分。

如果模块是同步/阻塞版本的快速端口,则可能不会发生这种情况,直到操作的某些部分完成并且所有队列在此之前可能为空,从而允许节点以静默方式退出。

这是一个偷偷摸摸的错误,也就是模块开发人员在开发过程中可能不会遇到的错误,因为在具有许多队列的繁忙系统中它会更少发生,因为它对于所有人来说都是罕见的他们在关键时刻是空的。

用户可能的修复/错误检测器是在可疑函数调用之前插入特殊的计时器事件。

答案 1 :(得分:37)

您可以使用setInterval发出setTimeout或重复超时。

如果要检查退出条件,还可以执行条件超时:

(function wait () {
   if (!SOME_EXIT_CONDITION) setTimeout(wait, 1000);
})();

将此代码放在代码的末尾,控制台将等待...等待......直到您希望它关闭。

答案 2 :(得分:8)

我的解决方案是实例化一个EventEmitter,并监听我的自定义事件。

var eventEmitter = new process.EventEmitter();

然后我从异步回调中调用了eventEmitter.emit

client.connect(function (err) {
    eventEmitter.emit('myevent', {something: "Bla"})
});

我脚本中的最后一件事是eventEmitter.on

eventEmitter.on('myevent', function(myResult){
  // I needed the result to be written to stdout so that the calling process could get it
  process.stdout.write(JSON.stringify(myResult));
});

Node将等到事件处理程序完成运行。

答案 3 :(得分:2)

基于@Todd的回答,我创建了一个单行。将其包含在脚本的开头,并在完成后设置done = true

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

示例:

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

someAsyncOperation().then(() => {
  console.log('Good to go!');
  done = true;
});

它是如何工作的?如果我们稍微扩展一下:

// Initialize the variable `done` to `undefined`
// Create the function wait, which is available inside itself
// Note: `var` is hoisted but `let` is not so we need to use `var`
var done = (function wait () {

  // As long as it's nor marked as done, create a new event+queue
  if (!done) setTimeout(wait, 1000);

  // No return value; done will resolve to false (undefined)
})();

答案 4 :(得分:0)

这是我的两分钱

async function main()
{
    await new Promise(function () {});
    console.log('This text will never be printed');
}

function panic(error)
{
    console.error(error);
    process.exit(1);
}

// https://stackoverflow.com/a/46916601/1478566
main().catch(panic).finally(clearInterval.bind(null, setInterval(a=>a, 1E9)));

答案 5 :(得分:0)

这就是我的方法。我让主入口点返回一个诺言,然后使用这个小包装程序确保只要未兑现诺言,节点就不会退出:

function wrapPromiseMain(entryPoint) {
    const pollTime = 1000000;
    let interval = setInterval(() => {}, pollTime);
    
    return entryPoint().finally(() => {clearInterval(interval)});
}

要使用它,只需承诺您的主入口点并将其作为参数传递给包装器即可:

function main() {

    // ... main stuff ...

    return doSomethingAsync();
}

wrapPromiseMain(main);

我发现它比基本的轮询循环更整洁,因为它会在承诺履行时自动取消计时器,因此不会增加任何额外的延迟。因此,如果您愿意,轮询时间基本上可以是永久的。

答案 6 :(得分:-1)

我确实查看了felixge/node-mysql库,但没有在API中看到对命令client.connect的引用。这是你想要做的实际通话(不是在这里挑剔)吗?无论如何,恕我直言,你需要更多地考虑如何设计Javascript,因为它使用的编程范例与大多数其他流行语言不同。

我在你的代码中看到的第一个问题是你没有定义回调,所以它实际上并不存在。我假设console.log(回调)未定义。从您的代码中,匿名函数是client.connect函数的“回调”。你必须在更高的范围内定义你所谓的'回调'。例如,我将定义一个函数myCallback,它存在于高于client.connect的匿名函数的范围内。查找Javacscript variable scope可能很有用。

    var myCallback(err, response) {
      if (err) {
        console.log('err:%s',err);
      } else {
        console.log('response:%s',response);
      }
    }

    client.connect(err, function(response) {
      // this anonymous function is the callback to client.connect, the var
      // 'callback' would be undefined.
      if (err) {
        myCallback(err);
        return; // Explicit call to return, else the lines below would run.
      } 
      myCallback(null, response);
    });

其次,如果你没有在Javascript中显式调用return,该函数将继续处理。我被myself咬了一口。最后,Javascript运行一个event-driven循环,这意味着它永远不会等待函数返回一个值,这就是我们首先拥有所有这些回调的原因。您可以强制Javascript以不同的方式运行,例如通过使用while循环直到条件为真。有关操作事件循环的各种策略,请参阅caolan的'async'库。过度使用这些方法的主要缺点是,当您可能应该使用更多回调并且只是重新思考程序的工作方式时,实际上会浪费CPU周期/阻塞。

答案 7 :(得分:-1)

请尝试这个。检查是否有帮助。

var client = new mysql.Client(options);
console.log('Icanhasclient');
var verbose;

if (!verbose) {
    return new Promise(function (resolve, reject) {
        client.connect(function (err) {
            if (err) {
                console.log(Error in connecting
                SQL ${err}
            )
                ;
                return reject(err);
            }
            verbose = client;
            return resolve(verbose);
        })
    })
} else {
    return new Promise(function (resolve) {
        resolve(verbose);
    })
}