Skip to main content

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 Promise
  • Promise.prototype.then can be called with a future
note

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)

tip

UnhandledRejection will be automatically consumed if reject is called before adding a .catch handler. You can safely use this without adding a explicit handler.