在非回调函数上调用promisify():"有趣"结果在节点。为什么呢?

时间:2018-03-02 04:47:02

标签: node.js promise async-await es6-promise

我在节点的promisify()函数中发现了一个奇怪的行为,我无法弄清楚它为什么要做它正在做的事情。

考虑以下脚本:

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var http = require('http')

var promisify = require('util').promisify

;(async () => {
  try {

    // UNCOMMENT THIS, AND NODE WILL QUIT
    // var f = function () { return 'Straight value' }
    // var fP = promisify(f)
    // await fP()

    /**
     * Create HTTP server.
     */
    var server = http.createServer()

    /**
     * Listen on provided port, on all network interfaces.
     */

    server.listen(3000)
    server.on('error', (e) => { console.log('Error:', e); process.exit() })
    server.on('listening', () => { console.log('Listening') })
  } catch (e) {
    console.log('ERROR:', e)
  }
})()

console.log('OUT OF THE ASYNC FUNCTION')

它是一个简单的自调用函数,可以启动节点服务器。 那很好。

现在......如果您取消注释" UNCOMMENT THIS"下的行,节点将退出而不运行服务器。

我知道我在调用回调的函数上使用promisify(),而是返回一个值。所以,我知道这本身就是一个问题。

然而......为什么节点刚刚退出 ......?

这很难调试 - 特别是当你有一个比一个小脚本更复杂的东西时。

如果将函数定义更改为实际调用回调的内容:

var f = function (cb) { setTimeout( () => { return cb( null, 'Straight value') }, 2000) }

一切都按预期工作......

更新

巨大的简化:

function f () {
  return new Promise(resolve => {
    console.log('AH')
  })
}

f().then(() => {
  console.log('Will this happen...?')
})

只会打印" AH"!

1 个答案:

答案 0 :(得分:4)

  

在非回调函数上调用promisify():节点中的“有趣”结果。为什么呢?

因为你允许node.js进入事件循环而无需做任何事情。由于没有正在运行的实时异步操作而且没有更多代码可以运行,因此node.js意识到没有其他任何事情可做,也无法运行其他任何东西,因此它会退出。

当您点击await并且node.js返回到事件循环时,没有任何东西可以保持node.js运行以便退出。没有定时器或开放套接字或任何使node.js保持运行的东西,因此node.js自动退出检测逻辑表明没有别的办法可以退出。

因为node.js是一个事件驱动的系统,如果你的代码返回到事件循环并且没有任何类型的异步操作(开放套接字,监听服务器,定时器,文件I / O操作,其他硬件)监听器等...),然后没有任何运行可以在事件队列中插入任何事件,并且队列当前为空。因此,node.js意识到永远不可能有任何方法在这个应用程序中运行任何更多的代码,所以它退出。这是内置于node.js中的自动行为。

fp()内部的真正异步操作会有某种套接字或定时器或打开的东西来保持进程运行。但是因为你的是假的,所以没有什么可以保持node.js运行。

如果在setTimeout()内放置f() 1秒钟,您将看到1秒后进程退出。因此,流程退出与承诺无关。这与你已经回到事件循环这一事实有关,但是你还没有开始任何可以使node.js运行的东西。

或者,如果您在异步函数的顶部放置setInterval(),您将同样发现该进程不会退出。

所以,如果你这样做会同样发生:

var f = function () { return 'Straight value' }
var fP = promisify(f);
fP().then(() => {
    // start your server here
});

或者这个:

function f() {
    return new Promise(resolve => {
       // do nothing here
    });
}

f().then(() => {
    // start your server here
});

问题不在于promisify()操作。这是因为你正在等待一个不存在的异步操作,因此node.js无事可做,它注意到没有任何事情要做,所以它会自动退出。拥有.then()处理程序的开放承诺并不能保持node.js运行。而是需要一些活动的异步操作(定时器,网络套接字,监听服务器,正在进行的文件I / O操作等)来保持node.js运行。

在这种特殊情况下,node.js基本上是正确的。您的承诺永远不会解决,没有其他任何东西排队等待运行,因此您的服务器将永远不会启动,您的应用程序中的任何其他代码都不会运行,因此继续运行实际上并没有用。没有任何事可做,也没办法让你的代码真正做任何事情。

  

如果将函数定义更改为实际调用回调的内容:

那是因为你使用了一个计时器,所以node.js在等待解析的承诺时实际上有所作为。没有调用.unref()的正在运行的计时器将阻止自动退出。

值得一读:How does a node.js process know when to stop?

仅供参考,你可以"关闭"或"绕过" node.js自动退出逻辑,只需在启动代码中的任何位置添加它:

// timer that fires once per day
let foreverInterval = setInterval(() => {
    // do nothing
}, 1000 * 60 * 60 * 24);

这总是给node.js做些什么,所以它永远不会自动退出。然后,如果您希望自己的流程退出,可以拨打clearInterval(foreverInterval)或者仅使用process.exit(0)强制执行。