javascript - 如何使用可以启动/停止的递归函数编写应用程序

标签 javascript node.js asynchronous recursion promise

我有一个应用程序需要连续运行一个功能。该函数返回一个 promise 。我希望应用程序在再次启动该功能之前等到 promise 得到解决。

此外,我的应用程序需要一个 startstop 函数来分别启动或停止该函数。

我这里有一个简化的例子:

class App {
  constructor() {
    this.running = false
    this.cycles = 0
  }

  start() {
    this.running = true
    this._run()
  }

  stop() {
    this.running = false
  }

  _run() {
    Promise.resolve().then(() => {
      this.cycles++
    }).then(() => {
      if (this.running) {
        this._run()
      }
    })
  }
}

module.exports = App

我的问题是,当我使用它时,setTimeout 似乎放弃了我。例如,如果我运行这个:

const App = require("./app")

const a = new App()

a.start()

console.log("a.start is not blocking...")

setTimeout(() => {
  console.log("This will never get logged")
  a.stop()
  console.log(a.cycles)
}, 500)

输出将是:

a.start is not blocking...

然后永远不会调用 setTimeout 回调中的代码。

我可以尝试在我的命令行上开始运行 node 并直接在 REPL 中输入,但是在我调用 a.start() 之后,终端卡住了,我可以不再输入任何内容。

这种东西看起来应该是一个很常见的模式。例如,Express 可以让您启动/停止服务器而不会出现这些问题。我需要做什么才能获得这种行为?

最佳答案

您的_run() 方法是无限的。它永远不会停止调用自身,除非其他代码可以运行并更改 this.running 的值,但仅使用 .then() 不足以可靠地允许您的其他 setTimeout() 代码运行,因为 .then() 以比事件队列中的计时器事件更高的优先级运行。

虽然 .then() 保证是异步的,但它将以比 setTimeout() 更高的优先级运行,这意味着您的递归调用会无限期地运行,而您的其他调用setTimeout() 永远不会运行,因此 this.running 永远不会改变。

相反,如果您使用一个简短的 setTimeout() 本身递归调用 _run(),那么您的另一个 setTimeout() 将得到一个跑的机会。而且,由于根本不需要在那里使用 promises,您可以删除它们:

改成这样:

class App {
  constructor() {
    this.running = false
    this.cycles = 0
  }

  start() {
    this.running = true
    this._run()
  }

  stop() {
    this.running = false
  }

  _run() {
      this.cycles++
      if (this.running) {
         // call recursively after a short timeout to allow other code
         // a chance to run
         setTimeout(this._run.bind(this), 0);
      }
  }
}

module.exports = App

有关 .then()setImmediate()nextTick() 之间的相对优先级的一些讨论,请参见其他答案:

Promise.resolve().then vs setImmediate vs nextTick

有关此主题的更多信息:

https://github.com/nodejs/node-v0.x-archive/pull/8325


广义的优先级层次结构似乎是:

.then()
nextTick()
other events already in the queue
setImmediate()
setTimeout()

因此,您可以从中看到 .then() 跳到队列中已有的其他事件前面,因此您的 setTimeout() 永远不会运行只要他们是 .then() 就等着走。

因此,如果您希望在下次调用 this._run() 之前允许队列中的其他计时器事件运行,您必须使用 setImmediate()setTimeout()。在这种情况下可能两者都有效,但由于其他事件是 setTimeout(),我想在这里使用 setTimeout() 可以保证安全,因为你知道一个新的 setTimeout() 回调不能跳到已经是挂起事件的前面。

关于javascript - 如何使用可以启动/停止的递归函数编写应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39906021/

相关文章:

node.js - Node JS 7.3.0 和 WebStorm 2016.3.2 : IDE always debugs with "--inspect"

c# - 在 ASP.NET C# 中异步发送电子邮件

javascript - 单击链接时停止重定向

javascript - Node 嵌套函数回调丢失 - Lex bot sessionAttributes

javascript - 如果用户没有回答所有问题,则带有反馈的 HTML 表单

javascript - 通知应用程序中的时区

mysql - Heroku 部署错误 :connection refused

javascript - 将 gulp-file 分成几个文件

ios - 最近的iOS中同步和异步的区别

javascript - 在 async/await 上返回多个变量