Spaces:
Sleeping
Sleeping
| # Deferred Promise | |
| The `DeferredPromise` class is a Promise-compatible abstraction that defers resolving/rejecting promises to another closure. This class is primarily useful when one part of your system establishes as promise but another part of your system fulfills it. | |
| > This class is conceptually inspired by the [`createDeferredPromise()`](https://github.com/nodejs/node/blob/696fd4b14fc34cc2d01497a3abd9bb441b89be50/lib/internal/util.js#L468-L477) internal utility in Node.js. Unlike the Node.js implementation, however, `DeferredProimse` _extends_ a native `Promise`, allowing the consumer to handle deferred promises like regular promises (no `.promise` instance nesting). | |
| ## Getting started | |
| ```sh | |
| npm install @open-draft/deferred-promise | |
| ``` | |
| ## Documentation | |
| - [**`createDeferredExecutor()`**](#createdeferredexecutor) | |
| - [`DeferredExecutor.state`](#deferredexecutorstate) | |
| - [`DeferredExecutor.resolve()`](#deferredexecutorresolve) | |
| - [`DeferredExecutor.reject()`](#deferredexecutorreject) | |
| - [`DeferredExecutor.rejectionReason`](#deferredexecutorrejectionreason) | |
| - [**Class: `DeferredPromise`**](#class-deferredpromise) | |
| - [`new DeferredPromise()`](#new-defferedpromise) | |
| - [`deferredPromise.state`](#deferredpromisestate) | |
| - [`deferredPromise.resolve()`](#deferredpromiseresolve) | |
| - [`deferredPromise.reject()`](#deferredpromisereject) | |
| - [`deferredPromise.rejectionReason`](#deferredpromiserejectionreason) | |
| --- | |
| ## `createDeferredExecutor()` | |
| Creates a Promise executor function that delegates its resolution to the current scope. | |
| ```js | |
| import { createDeferredExecutor } from '@open-draft/deferred-promise' | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| executor.resolve('hello') | |
| // executor.reject(new Error('Reason')) | |
| ``` | |
| Deferred executor allows you to control any promise remotely and doesn't affect the Promise instance in any way. Similar to the [`DeferredPromise`](#class-deferredpromise) instance, the deferred executor exposes additional promise properties like `state`, `rejectionReason`, `resolve`, and `reject`. In fact, the `DeferredPromise` class is implemented on top of the deferred executor. | |
| ```js | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| executor.reject('reason') | |
| nextTick(() => { | |
| console.log(executor.rejectionReason) // "reason" | |
| }) | |
| ``` | |
| ### `DeferredExecutor.state` | |
| - `<"pending" | "fulfilled" | "rejected">` **Default:** `"pending"` | |
| ```js | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| console.log(executor.state) // "pending" | |
| ``` | |
| Calling [`resolve()`](#deferredexecutorresolve) and [`reject()`](#deferredexecutorreject) methods of the executor transitions the state to "fulfilled" and "rejected" respectively. | |
| ### `DeferredExecutor.resolve()` | |
| Resolves the promise with a given value. | |
| ```js | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| console.log(executor.state) // "pending" | |
| executor.resolve() | |
| // The promise state is still "pending" | |
| // because promises are settled in the next microtask. | |
| console.log(executor.state) // "pending" | |
| nextTick(() => { | |
| // In the next microtask, the promise's state is resolved. | |
| console.log(executor.state) // "fulfilled" | |
| }) | |
| ``` | |
| ### `DeferredExecutor.reject()` | |
| Rejects the promise with a given reason. | |
| ```js | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| executor.reject(new Error('Failed to fetch')) | |
| nextTick(() => { | |
| console.log(executor.state) // "rejected" | |
| console.log(executor.rejectionReason) // Error("Failed to fetch") | |
| }) | |
| ``` | |
| You can access the rejection reason of the promise at any time by the [`rejectionReason`](#deferredexecutorrejectionreason) property of the deferred executor. | |
| ### `DeferredExecutor.rejectionReason` | |
| Returns the reason of the promise rejection. If no reason has been provided to the `reject()` call, `undefined` is returned instead. | |
| ```js | |
| const executor = createDeferredExecutor() | |
| const promise = new Promise(executor) | |
| promise.reject(new Error('Internal Server Error')) | |
| nextTick(() => { | |
| console.log(promise.rejectionReason) // Error("Internal Server Error") | |
| }) | |
| ``` | |
| --- | |
| ## Class: `DeferredPromise` | |
| ### `new DefferedPromise()` | |
| Creates a new instance of a deferred promise. | |
| ```js | |
| import { DeferredPromise } from '@open-draft/deferred-promise' | |
| const promise = new DeferredPromise() | |
| ``` | |
| A deferred promise is a Promise-compatible class that constructs a regular Promise instance under the hood, controlling it via the [deferred executor](#createdeferredexecutor). | |
| A deferred promise is fully compatible with the regular Promise, both type- and runtime-wise, e.g. a deferred promise can be chained and awaited normally. | |
| ```js | |
| const promise = new DefferredPromise() | |
| .then((value) => value.toUpperCase()) | |
| .then((value) => value.substring(0, 2)) | |
| .catch((error) => console.error(error)) | |
| await promise | |
| ``` | |
| Unlike the regular Promise, however, a deferred promise doesn't accept the `executor` function as the constructor argument. Instead, the resolution of the deferred promise is deferred to the current scope (thus the name). | |
| ```js | |
| function getPort() { | |
| // Notice that you don't provide any executor function | |
| // when constructing a deferred promise. | |
| const portPromise = new DeferredPromise() | |
| port.on('open', (port) => { | |
| // Resolve the deferred promise whenever necessary. | |
| portPromise.resolve(port) | |
| }) | |
| // Return the deferred promise immediately. | |
| return portPromise | |
| } | |
| ``` | |
| Use the [`resolve()`](#deferredpromiseresolve) and [`reject()`](#deferredpromisereject) methods of the deferred promise instance to resolve and reject that promise respectively. | |
| ### `deferredPromise.state` | |
| See [`DeferredExecutor.state`](#deferredexecutorstate) | |
| ### `deferredPromise.resolve()` | |
| See [`DeferredExecutor.resolve()`](#deferredexecutorresolve) | |
| ### `deferredPromise.reject()` | |
| See [`DeferredExecutor.reject()`](#deferredexecutorreject) | |
| ### `deferredPromise.rejectionReason` | |
| See [`DeferredExecutor.rejectionReason`](#deferredexecutorrejectionreason) | |
| --- | |
| ## Mentions | |
| - [Jonas Kuske](https://github.com/jonaskuske) for the phenomenal work around improving Promise-compliance. | |