From 5c55770dc681e7855fe960cf6a725d4c7be4e95f Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 18 Jul 2024 02:34:38 +0100 Subject: [PATCH] Clarify then() handler's lifetime (#34872) * Clarify then() handler's lifetime * clarify it's a mutating method * handler -> handlers * Update files/en-us/web/javascript/reference/global_objects/promise/then/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Correctly initialize pending promise * Update files/en-us/web/javascript/reference/global_objects/promise/then/index.md * Update index.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Joshua Chen --- .../reference/global_objects/promise/then/index.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/files/en-us/web/javascript/reference/global_objects/promise/then/index.md b/files/en-us/web/javascript/reference/global_objects/promise/then/index.md index 217a6560fdde547..c70116b223a7677 100644 --- a/files/en-us/web/javascript/reference/global_objects/promise/then/index.md +++ b/files/en-us/web/javascript/reference/global_objects/promise/then/index.md @@ -7,7 +7,7 @@ browser-compat: javascript.builtins.Promise.then {{JSRef}} -The **`then()`** method of {{jsxref("Promise")}} instances takes up to two arguments: callback functions for the fulfilled and rejected cases of the `Promise`. It immediately returns another {{jsxref("Promise")}} object, allowing you to [chain](/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining) calls to other promise methods. +The **`then()`** method of {{jsxref("Promise")}} instances takes up to two arguments: callback functions for the fulfilled and rejected cases of the `Promise`. It stores the callbacks within the promise it is called on and immediately returns another {{jsxref("Promise")}} object, allowing you to [chain](/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining) calls to other promise methods. {{EmbedInteractiveExample("pages/js/promise-then.html")}} @@ -57,7 +57,16 @@ The `then()` method schedules callback functions for the eventual completion of For more information about the `onRejected` handler, see the {{jsxref("Promise/catch", "catch()")}} reference. -`then()` returns a new promise object. If you call the `then()` method twice on the same promise object (instead of chaining), then this promise object will have two pairs of settlement handlers. All handlers attached to the same promise object are always called in the order they were added. Moreover, the two promises returned by each call of `then()` start separate chains and do not wait for each other's settlement. +`then()` returns a new promise object but mutates the promise object it's called on, appending the handlers to an internal list. Therefore the handler is retained by the original promise and its lifetime is at least as long as the original promise's lifetime. For example, the following example will eventually run out of memory even though the returned promise is not retained: + +```js +const pendingPromise = new Promise(() => {}); +while (true) { + pendingPromise.then(doSomething); +} +``` + +If you call the `then()` method twice on the same promise object (instead of chaining), then this promise object will have two pairs of settlement handlers. All handlers attached to the same promise object are always called in the order they were added. Moreover, the two promises returned by each call of `then()` start separate chains and do not wait for each other's settlement. [Thenable](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables) objects that arise along the `then()` chain are always [resolved](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise#the_resolve_function) — the `onFulfilled` handler never receives a thenable object, and any thenable returned by either handler are always resolved before being passed to the next handler. This is because when constructing the new promise, the `resolve` and `reject` functions passed by the `executor` are saved, and when the current promise settles, the respective function will be called with the fulfillment value or rejection reason. The resolving logic comes from the `resolve` function passed by the {{jsxref("Promise/Promise", "Promise()")}} constructor.