会在许多承诺链之间共享全局解决的承诺会造成内存泄漏还是性能下降?

时间:2016-11-18 18:29:47

标签: javascript memory-leaks bluebird es6-promise

我常常陷入这种模式:

function makeSureDataWasFetched () {
  if (dataAlreadyFetched) {
    // Creating a new empty resolved promise object and returning it
    // to keep the function interface consistent
    return Promise.resolve()
  } else {
    return fetchData()
  }
}

因此,为了避免每次重新创建一个新的空解决的承诺对象,我一直在玩分享一个独特的,全球解决的承诺的想法

const resolved = Promise.resolve()

// then, re-use this resolved promise object all over the place
function makeSureDataWasFetched () {
  if (dataAlreadyFetched) {
    // Return the shared resolved promise object,
    // sparing the creation of a new resolved promise object
    return resolved
  } else {
    return fetchData()
  }
}

function makeSureSomethingElseWasFetched () {
  if (thatSomethingElseWasAlreadyFetched) {
    return resolved
  } else {
    return fetchSomethingElse()
  }
}

// etc

在整个应用程序中被引用,这个已解决的承诺永远不会被垃圾回收。 因此,如果它保留对使用它的promise链的一些引用,那些也不会被垃圾收集,这会产生内存泄漏,对吗?

所以我的问题是:这种全球解决的承诺是否会引用Bluebird实施中所有依赖承诺链的参考?在香草ES6承诺? 如果没有,那么每次创造新的已解决承诺的成本是否有任何性能下降的平衡?

1 个答案:

答案 0 :(得分:2)

  

将在许多承诺链之间共享全球解决的承诺   造成内存泄漏或性能下降?

没有

重用和共享全局解析的承诺只会使单个全局解决的承诺永远被垃圾收集。它不会影响可能链接到它的其他promise的垃圾收集。当它们不再像通常那样可以到达时,它们将被垃圾收集。

现在,目前尚不清楚共享全球解决的承诺有什么优势。它不应该被要求。只要您想要已经解决的承诺,您就可以使用Promise.resolve()创建一个承诺,然后您的代码不依赖于共享的全局,并且可以更加模块化。

  

所以我的问题是:这种全球解决的承诺会保持一个   参考Bluebird中所有依赖承诺链的人   执行?

没有

  

在香草ES6承诺?

没有

  

如果没有,那么是否有任何性能下行抵消平衡   每次创造新的解决承诺的成本是多少?

您在询问过早的微观优化,这绝不是一个好主意。如果在将来的某个时候,你想要完全优化你的代码的性能,那么你就可以进行分析和测量,我可以向你保证,你会找到100个你可以使用的东西,这会影响你的代码,远远超过尝试分享全球以实现已解决的承诺。

为了帮助您了解谁参考了承诺链中的内容以及什么时候可以收集垃圾,让我来描述链接的工作原理。假设您有以下代码:

f1().then(f2).then(f3)

f1f2都会返回我们称之为P1P2的承诺。

所以,这是进展:

  1. 致电f1()并返回承诺P1
  2. 致电P1.then(f2),它会返回一个新的承诺,我们会致电P3
  3. 致电P3.then(f3),它会返回一个我们称之为'P4'的新承诺。
  4. 然后,在将来的某个时刻,P1将被解析并触发它调用其.then()处理程序。
  5. f2被调用并返回promise P2
  6. 当promise .then()代码的内部从f2 .then()处理程序获得返回值作为返回值时,它会检测到此返回值是一个promise然后链新承诺P2P3。它通过将自己的.then()处理程序添加到P2来实现,因此它可以跟踪其状态。在P3的内部,它将这个新的.then()处理程序添加到在P3可以解决之前必须发生的事情列表中。请注意,P2P3之间没有直接引用。他们甚至没有直接引用彼此。相反,P3正在等待要调用的特定.then()处理程序(恰好附加到P2)。
  7. 然后,将来某个时间P2会解决。
  8. 这将调用在步骤6中建立的内部.then()处理程序,并清除对该.then()处理程序的任何引用。这告诉P3它现在可以自行解决,并将其间接引用取消链接到P2。此时此承诺链中的任何内容都不再引用P2,因此如果代码中的其他地方没有其他引用,则可以进行GCed。
  9. P3结算时,它会调用其.then()处理程序,这些处理程序将执行f3告诉您的代码现在已完成承诺链。
  10. 所以,从此我希望你能看到链式承诺实际上并不存储彼此的引用。父承诺以子承诺上的.then()处理程序结束,这使得子承诺在调用.then()处理程序并调用.then()处理程序之后保持GCed,即使是间接的两个promises之间的连接被切断,每个promise都可以独立用于GC(只要其他代码中没有其他引用)。

    根据你的问题,如果P2碰巧是你已经解决的共享的全局承诺,那么第6步就会添加一个.then()处理程序,它将在下一个tick上调用(因为底层的promise已经解决了)并且一旦.then()处理程序被调用,就再也没有P2的连接,因此它是一个持久的全局与否并不重要。