【问题标题】:How to know when the Promise is actually resolved in Node.js?如何知道 Promise 何时在 Node.js 中实际解决?
【发布时间】:2016-12-14 16:53:19
【问题描述】:

当我们在 nodejs 中使用 Promise 时,给定一个 Promise p,我们无法通过在“then”回调中记录 currentTime 来知道 Promise p 何时真正解决。

为了证明这一点,我编写了下面的测试代码(使用 CoffeeScript):

# the procedure we want to profile
getData = (arg) ->
    new Promise (resolve) ->
        setTimeout ->
            resolve(arg + 1)
        , 100

# the main procedure
main = () ->
    beginTime = new Date()
    console.log beginTime.toISOString()
    getData(1).then (r) ->
        resolveTime = new Date()
        console.log resolveTime.toISOString()
        console.log resolveTime - beginTime
    cnt = 10**9
    --cnt while cnt > 0
    return cnt

main()

当您运行上述代码时,您会注意到 resolveTime(您的代码运行到回调函数的时间)比 beginTime 晚了 100 毫秒。

所以如果我们想知道 Promise 何时真正解决,如何?


我想知道确切的时间,因为我正在通过日志记录进行一些分析。当我在黑盒之外进行一些分析时,我无法修改 Promise p 的实现。

那么,是否有类似 promise.onUnderlyingConditionFulfilled(callback) 之类的函数,或者任何其他方式可以实现这一点?

【问题讨论】:

  • 你为什么想知道那个时间?
  • 实际!== 预测。没有惊喜。
  • @Bergi 因为我正在通过日志记录进行一些分析,我需要知道 http 请求何时实际响应,而不受其他代码的影响。
  • @Roamer-1888 我不明白你的意思...对不起我的英语水平不佳
  • @luochen1990 为此,请使用性能计时 API。它可以让您获得 http 请求的时间戳,甚至当时还没有处于活动状态。

标签: node.js promise es6-promise


【解决方案1】:

这是因为您有一个繁忙的循环,显然比您的计时器花费的时间更长:

cnt = 10**9
--cnt while cnt > 0

node.js 中的 Javascript 是单线程和事件驱动的。它一次只能做一件事,它将完成当前正在做的事情,然后才能为setTimeout() 发布的事件提供服务。因此,如果您的繁忙循环(或任何其他长时间运行的 Javascript 代码)花费的时间比您为计时器设置的时间长,则在其他 Javascript 代码完成之前,计时器将无法运行。 “单线程”意味着 node.js 中的 Javascript 一次只做一件事,它会等到一件事将控制权返回给系统,然后才能为下一个等待运行的事件提供服务。

所以,这是您代码中的事件顺序:

  1. 它调用setTimeout() 来安排从现在起100ms 的定时器回调。
  2. 然后进入繁忙循环。
  3. 当它处于忙碌循环中时,setTimeout() 计时器在 JS 实现内部触发,并将一个事件插入到 Javascript 事件队列中。该事件目前无法运行,因为 JS 解释器仍在运行繁忙循环。
  4. 然后它最终完成繁忙循环并将控制权返回给系统。
  5. 完成后,JS 解释器会检查事件队列以查看是否有任何其他事件需要服务。它找到计时器事件并对其进行处理并调用setTimeout() 回调。
  6. 该回调解决了触发 .then() 处理程序被调用的承诺。

注意:由于 Javascript 的单线程性和事件驱动的特性,Javascript 中的计时器不能保证在您安排它们时准确地被调用。它们将尽可能接近执行,但如果其他代码在它们触发时正在运行,或者如果它们在您前面的事件队列中有很多项目,则该代码必须在计时器回调实际执行之前完成。

所以如果我们想知道 Promise 何时真正解决,如何?

当你的繁忙循环完成时,承诺就解决了。它并没有在 100 毫秒时完全解决(因为您的繁忙循环显然需要超过 100 毫秒才能运行)。如果您想确切知道承诺何时解决,您只需在调用resolve() 的位置登录setTimeout() 回调。这将准确地告诉您承诺何时解决,尽管它与您现在登录的位置几乎相同。延迟的原因是繁忙的循环。


根据您的 cmets,您似乎想以某种方式准确测量 Promise 中实际调用 resolve() 的时间,但您说您无法修改 getData() 中的代码。你不能直接这样做。如您所知,您可以测量.then() 处理程序何时被调用,这可能在resolve() 被调用后不超过几毫秒。

您可以用您自己的实现替换 Promise 基础结构,然后您可以直接检测 resolve() 回调,但是替换或挂钩 Promise 实现可能会影响事情的时间,甚至不仅仅是从 .then() 处理程序进行测量。

所以,在我看来,您只是过度限制了问题。您想测量某些代码内部何时发生某些事情,但您不允许在该代码中进行任何检测。这只给你两个选择:

  1. 替换 Promise 实现,以便您可以直接检测 resolve()
  2. .then() 被触发时进行测量。

第一个选项可能存在 heisenberg 不确定性问题,如果替换或挂钩 promise 实现,您可能对时间的影响超过了应有的程度。

第二个选项测量在实际.resolve() 之后稍稍发生的事件。选择哪一个听起来最接近你真正想要的。

【讨论】:

  • ……当然,整个超时回调和承诺解决过程被繁忙循环延迟,不仅仅是承诺回调。我希望在setTimeout 回调和then 回调的执行之间resolveTime 应该没有什么区别。
  • @Bergi - 我不确定你到底想表达什么观点。我并不是想说resolve() 被调用和.then() 处理程序被调用之间存在很大差异。我修改了最后两句话以确保清楚。我的最后一段只是试图回答所提出的字面问题。前面的所有段落都解释了延迟的原因。
  • 是的,感谢您澄清将其移到那里几乎不会改变行为。
  • 我想知道确切的时间,因为我正在做一些分析,我的问题是关于“如何获得正确的时间?”,而不是关于“为什么会发生这种情况”,我实际上知道为什么会发生,上面的代码就是为了解释原因。当我在黑盒之外进行一些分析时,我无法修改 Promise p 的实现。
  • @luochen1990 - 您在询问如何衡量代码中您无法修改的内容何时发生。你不能从纯 Javascript 中做到这一点。当.then() 处理程序被调用时,你可以得到尽可能接近,它将在毫秒左右。
猜你喜欢
  • 2014-05-05
  • 2020-11-26
  • 2018-07-31
  • 2016-10-14
  • 2020-11-03
  • 2012-12-22
  • 2015-01-29
相关资源
最近更新 更多