【问题标题】:Dealing with forgotten promises in JavaScript处理 JavaScript 中被遗忘的 promise
【发布时间】:2019-01-28 11:05:48
【问题描述】:

这里有一个例子,希望能更容易理解这个问题。

var listen = document.querySelector("#listen"),
  cancel = document.querySelector("#cancel"),
  submit = document.querySelector("#submit");

var promiseResolve = null;

listen.addEventListener("click", startListening);
cancel.addEventListener("click", abort);
submit.addEventListener("click", onSubmitClick);
submit.disabled = true;

function startListening() {
  submit.disabled = false;
  listen.disabled = true;
  new Promise(function(resolve) {
    promiseResolve = resolve;
  }).then(onSubmit);
}

function abort() {
  listen.disabled = false;
  submit.disabled = true;
  promiseResolve = null;
}

function onSubmitClick() {
  if (promiseResolve) promiseResolve();
}

function onSubmit() {
  console.log("Done");
  abort();
}
<button id="listen">Listen</button>
<button id="submit">Submit</button>
<button id="cancel">Cancel</button>

在上面的脚本中,有一个动作 (listen) 将在 Promise 的帮助下启用另一个动作 (submit)。但是可以使用cancel 操作取消该流程,将大部分代码返回到其原始状态。 cancel 操作仅将 Promiseresolve 的引用设置为 null,这意味着承诺将永远处于不确定状态,因为它永远不会被解决或拒绝。所以这些是我的问题:

  • 这种方法正确吗?
  • 是否会多次执行此操作,占用同样大量的资源?
  • 我是否还应该保留对拒绝函数的引用并在cancel 操作中调用它?

我知道在上面的例子中,同样的结果可以通过 使用boolean 标志检查是否已按下监听按钮 在提交之前,但就像我说的这只是一个例子,所以我 可以更容易地解释问题。

【问题讨论】:

  • 我相信理论上它应该被 GC 处理。但它使用 Promises 来完成它们的设计目的。要真正获得 Promise 的好处,最好考虑如何以同步方式执行此操作,然后以这种方式编写代码。例如。你可以让你的 startListening 变成一个承诺,然后你把它放在一个循环中,对于中止我会生成一个自定义错误,比如EAbort。经常有很多关于可取消承诺的讨论,但我不确定它处于什么状态。我相信谷歌不喜欢这个想法,这可能是因为可取消的承诺有点像任务终止。
  • 查看您的代码,您不再有引用,您这样做了 -> promiseResolve = null; 如果引用无法返回根/全局,则 GC 通过查看引用来工作,附注。计时器等也是全球性的。然后垃圾收集器将处理。 GC 甚至足够聪明地 GC a = b; b = a,这是 C++ 领域中 interfaces 之类的引用计数内存管理的限制。
  • @Keith 我明白你在说什么,请查看我对答案的评论以了解我的问题。

标签: javascript ecmascript-6 promise


【解决方案1】:

this回答中提到的;

大多数使用 Promise 的代码都希望它们在某些时候解决或拒绝 指向未来(这就是首先使用承诺的原因)。 如果他们不这样做,那么该代码通常永远无法完成其工作。

您可能有一些其他代码来完成 为那项任务而努力,而承诺却被放弃了 做它的事。如果你这样做,Javascript中没有内部问题 就是这样,但这不是 promise 的设计方式,而是 通常不是承诺的消费者期望它们如何工作。

未能解决或拒绝承诺不会导致 Javascript 出现问题,但无论如何这是一种不好的做法。如果它永远不会解决,您的应用程序无法确定 Promise 发生了什么。与其让 Promise 陷入困境,不如返回一个类似错误消息的值,并让 Promise 过滤错误消息的结果。如果发现错误,则拒绝()该承诺,以便应用程序可以确定下一步。

var listen = document.querySelector("#listen"),
  cancel = document.querySelector("#cancel"),
  submit = document.querySelector("#submit");

var promiseResolve = null;

listen.addEventListener("click", startListening);
cancel.addEventListener("click", onCancelClick);
submit.addEventListener("click", onSubmitClick);
submit.disabled = true;

function startListening() {
  submit.disabled = false;
  listen.disabled = true;
  new Promise(function(resolve, reject) {
    promiseResolve = (error) => {
       if (error) { reject(error); } else { resolve(); }
    };
  }).then(onSubmit)
  .catch(error => onError(error));
}

function abort() {
  listen.disabled = false;
  submit.disabled = true;
  promiseResolve = null;
}

function onSubmitClick() {
  if (promiseResolve) promiseResolve();
}

function onCancelClick() {
  if (promiseResolve) promiseResolve("Cancelled!");
}

function onSubmit() {
  console.log("Done");
  abort();
}

function onError(error) {
  console.warn(error);
  abort();
}
<button id="listen">Listen</button>
<button id="submit">Submit</button>
<button id="cancel">Cancel</button>

【讨论】:

  • 为什么Promise 会得到garbagecollected?我实际上从未保留对Promise 本身的引用,只有resolve 函数,我不相信它引用了Promise。如果 Promise 在我引用该函数时没有被垃圾收集,为什么在失去对不相关对象的引用后会有任何区别?
  • @nickzoum 垃圾收集器会在对象仍然存在时清理对象,但没有代码具有该对象的剩余引用,例如当您调用 myObject = null 时。然后 GC 可以安全地回收该对象的内存,因为没有代码可以再次到达该对象(即使在调用 GC 之前它在技术上仍然存在)。另见this 文章。
  • 你基本上已经回答了你自己的问题,..when i had the reference,这是关键部分,如果你在 JS 中没有引用某些东西,就会导致某种形式的全局,它将获得 GC'ed。这就是为什么在 JS 中你通常不必担心这些事情。话虽如此,仍然有可能发生内存泄漏,但这通常是通过附加到您不再需要的 addEventListener、setIntervals 等。这就是为什么它们是 clearInterval、removeEventListener 等。
  • @Keith 我仍然觉得,你还没有完全理解我的问题。在问题的代码中,我从不保留对承诺的引用,这是否意味着即使我不调用 cancel 操作,承诺也会被垃圾收集?如果不是,为什么在 cancel 操作之后承诺会被垃圾回收?
  • @nickzoum This 问题(和答案)似乎表明没有参考的承诺,也没有任何参考它的解决或拒绝功能,是 GC'ed。由于您的代码仍然引用了它的解析函数,因此不能对它进行 GC。另请参阅this 文章,尤其是“您忘记的参考资料”部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多