Future
Overview
A promising future, though it may never come.
Sometimes you may need to create a promise but resolve it later in other places. Unfortunately Promise
constructor only accepts callbacks and you need to manually hold a reference to resolve
or reject
like this:
class Deferred<T> {
public promise: Promise<T>
private _resolve!: (value: T) => void
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve
})
}
resolve(value: T): void {
this._resolve(value)
}
}
let resolver
const promise = new Promise((resolve) => {
resolver = resolve
})
This is not an individual case. The pattern widely exists amongst JavaScript projects, even the most well-known ones including npm (in test codes), electron, webpack and vscode (here and here).
With Future, you only write
import { Future, timers } from 'flowp'
const future = new Future<string>()
fetch('https://example.com').then(() => future.resolve('voila! 🐳'))
timers.sleep(() => future.reject(new Error('whoops, timed out XD')), 1000)
const result = await future
flowp itself is powered by Futures and you can refer to its source code to see how it can be used. And futures are real promises, so the following statements are true:
future instanceof Promise
future.then()
returns a normal PromisePromise.prototype.then
can be called with a future
Futures will not prevent a node.js program from exiting because it's not executing any code. It does not create an "open handle", but you can await a future at top level.
Class
class Future<T = unknown> extends Promise<T>
Where:
T
: type of its fulfilled value, defaults to unknown
Promise<T>
: Future inherits all methods and properties from JavaScripts's native Promise
Properties
pending
get pending(): boolean
Check if future is neither fulfilled nor rejected.
fulfilled
get fulfilled(): boolean
Check if future has been fullfilled.
rejected
get rejected(): boolean
Check if future has been rejected.
Methods
constructor
constructor()
Constructs a new Future, no parameters are required.
resolve
get resolve(): (value: T | PromiseLike<T>) => void
Resolves the promise with given value. Passing an PromiseLike
fulfills it with resolved value, the same way as Promise.prototype.then
.
It is actually a getter that returns a bound
version of resolver so future.resolve.bind(future)
is not required. Another rationale is that resolve
does not exist on the class level, but belongs to each future instance
reject
get reject(): (error?: unknown) => void
reject the future with given value.
the method has already bound to this
, so you can write emitter.on('error', future.reject)
UnhandledRejection
will be automatically consumed if reject
is called before adding a .catch
handler. You can safely use this without adding a explicit handler.