From 65151916d0bd24bcb2dab686825b65ca84600d7c Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 19 Jul 2024 18:04:08 -0400 Subject: [PATCH] Reference for stage 3 promise-try (#34929) --- .../reference/global_objects/promise/index.md | 2 + .../global_objects/promise/resolve/index.md | 2 + .../global_objects/promise/try/index.md | 143 ++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 files/en-us/web/javascript/reference/global_objects/promise/try/index.md diff --git a/files/en-us/web/javascript/reference/global_objects/promise/index.md b/files/en-us/web/javascript/reference/global_objects/promise/index.md index 3c0317d1b1cd255..f80a6c8f09e077c 100644 --- a/files/en-us/web/javascript/reference/global_objects/promise/index.md +++ b/files/en-us/web/javascript/reference/global_objects/promise/index.md @@ -192,6 +192,8 @@ Note that JavaScript is [single-threaded](/en-US/docs/Glossary/Thread) by nature - : Returns a new `Promise` object that is rejected with the given reason. - {{jsxref("Promise.resolve()")}} - : Returns a `Promise` object that is resolved with the given value. If the value is a thenable (i.e. has a `then` method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise, the returned promise will be fulfilled with the value. +- {{jsxref("Promise.try()")}} + - : Takes a callback of any kind (returns or throws, synchronously or asynchronously) and wraps its result in a `Promise`. - {{jsxref("Promise.withResolvers()")}} - : Returns an object containing a new `Promise` object and two functions to resolve or reject it, corresponding to the two parameters passed to the executor of the {{jsxref("Promise/Promise", "Promise()")}} constructor. diff --git a/files/en-us/web/javascript/reference/global_objects/promise/resolve/index.md b/files/en-us/web/javascript/reference/global_objects/promise/resolve/index.md index b86fcb3e8a3aff3..c752d41a1c538f8 100644 --- a/files/en-us/web/javascript/reference/global_objects/promise/resolve/index.md +++ b/files/en-us/web/javascript/reference/global_objects/promise/resolve/index.md @@ -32,6 +32,8 @@ A {{jsxref("Promise")}} that is resolved with the given value, or the promise pa `Promise.resolve()` _resolves_ a promise, which is not the same as fulfilling or rejecting the promise. See [Promise description](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#description) for definitions of the terminology. In brief, `Promise.resolve()` returns a promise whose eventual state depends on another promise, thenable object, or other value. +> **Note:** If evaluating the `value` expression may synchronously throw an error, this error won't be caught and wrapped in a rejected promise by `Promise.resolve()`. Consider using {{jsxref("Promise/try", "Promise.try(() => value)")}} in this case. + `Promise.resolve()` is generic and supports subclassing, which means it can be called on subclasses of `Promise`, and the result will be a promise of the subclass type. To do so, the subclass's constructor must implement the same signature as the [`Promise()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) constructor — accepting a single `executor` function that can be called with the `resolve` and `reject` callbacks as parameters. `Promise.resolve()` special-cases native `Promise` instances. If `value` belongs to `Promise` or a subclass, and `value.constructor === Promise`, then `value` is directly returned by `Promise.resolve()`, without creating a new `Promise` instance. Otherwise, `Promise.resolve()` is essentially a shorthand for `new Promise((resolve) => resolve(value))`. diff --git a/files/en-us/web/javascript/reference/global_objects/promise/try/index.md b/files/en-us/web/javascript/reference/global_objects/promise/try/index.md new file mode 100644 index 000000000000000..88a2f1d8f76615f --- /dev/null +++ b/files/en-us/web/javascript/reference/global_objects/promise/try/index.md @@ -0,0 +1,143 @@ +--- +title: Promise.try() +slug: Web/JavaScript/Reference/Global_Objects/Promise/try +page-type: javascript-static-method +browser-compat: javascript.builtins.Promise.try +--- + +{{JSRef}} + +The **`Promise.try()`** static method takes a callback of any kind (returns or throws, synchronously or asynchronously) and wraps its result in a {{jsxref("Promise")}}. + +## Syntax + +```js-nolint +Promise.try(func) +``` + +### Parameters + +- `func` + - : A function that is called synchronously with no arguments. It can do anything—either return a value, throw an error, or return a promise. + +### Return value + +A {{jsxref("Promise")}} that is: + +- Already fulfilled, if `func` synchronously returns a value. +- Already rejected, if `func` synchronously throws an error. +- Asynchronously fulfilled or rejected, if `func` returns a promise. + +## Description + +You may have an API that takes a callback. The callback may be synchronous or asynchronous. You want to handle everything uniformly by wrapping the result in a promise. The most straightforward way might be {{jsxref("Promise/resolve", "Promise.resolve(func())")}}. The problem is that if `func()` synchronously throws an error, this error would not be caught and turned into a rejected promise. The correct way to do so is the following, which `Promise.try(func)` is exactly equivalent to: + +```js +new Promise((resolve) => resolve(func())); +``` + +Except that `Promise.try()` is perhaps more concise and readable. To be more exact, the following is a more faithful representation of the implementation of `Promise.try()` (although it should still not be used as a polyfill): + +```js +new Promise((resolve, reject) => { + try { + resolve(func()); + } catch (error) { + reject(error); + } +}); +``` + +For the built-in `Promise()` constructor, errors thrown from the executor are automatically caught and turned into rejections, so these two examples are equivalent. + +Note that `Promise.try()` is _not_ equivalent to this, despite being highly similar: + +```js +Promise.resolve().then(func); +``` + +The difference is that the callback passed to {{jsxref("Promise/then", "then()")}} is always called asynchronously, while the executor of the `Promise()` constructor is called synchronously. `Promise.try` also calls the function synchronously, and resolves the promise immediately if possible. + +`Promise.try()`, combined with {{jsxref("Promise/catch", "catch()")}} and {{jsxref("Promise/finally", "finally()")}}, can be used to handle both synchronous and asynchronous errors in a single chain, and make promise error handling appear almost like synchronous error handling. + +## Examples + +### Using Promise.try() + +The following example takes a callback, "lifts" it into a promise, handles the result, and does some error handling: + +```js +function doSomething(action) { + return Promise.try(action) + .then((result) => console.log(result)) + .catch((error) => console.error(error)) + .finally(() => console.log("Done")); +} + +doSomething(() => "Sync result"); + +doSomething(() => { + throw new Error("Sync error"); +}); + +doSomething(async () => "Async result"); + +doSomething(async () => { + throw new Error("Async error"); +}); +``` + +In async/await, the same code would look like this: + +```js +async function doSomething(action) { + try { + const result = await action(); + console.log(result); + } catch (error) { + console.error(error); + } finally { + console.log("Done"); + } +} +``` + +### Calling try() on a non-Promise constructor + +`Promise.try()` is a generic method. It can be called on any constructor that implements the same signature as the `Promise()` constructor. For example, we can call it on a constructor that passes `console.log` as the `resolve` and `reject` functions to `executor`: + +```js +class NotPromise { + constructor(executor) { + // The "resolve" and "reject" functions behave nothing like the native + // promise's, but Promise.try() just calls resolve + executor( + (value) => console.log("Resolved", value), + (reason) => console.log("Rejected", reason), + ); + } +} + +const p = Promise.try.call(NotPromise, () => "hello"); +// Logs: Resolved hello + +const p2 = Promise.try.call(NotPromise, () => { + throw new Error("oops"); +}); +// Logs: Rejected Error: oops +``` + +## Specifications + +{{Specifications}} + +## Browser compatibility + +{{Compat}} + +## See also + +- [Polyfill of `Promise.try` in `core-js`](https://github.com/zloirock/core-js#promisetry) +- [Using promises](/en-US/docs/Web/JavaScript/Guide/Using_promises) guide +- {{jsxref("Promise")}} +- [`Promise()` constructor](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise)