SkillAgentSearch skills...

Promise

Light and type-safe binding to JS promises

Install / Use

/learn @aantron/Promise
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Promise     NPM link Travis status Coverage

A lightweight, type-safe binding to JS promises:

Js.log(Promise.resolved("Hello"));  /* Promise { 'Hello' } */

Promise.resolved("Hello")
->Promise.map(s => s ++ " world!")
->Promise.get(s => Js.log(s));      /* Hello world! */

As you can see on the first line, Promise.t maps directly to familiar JS promises from your JS runtime. That means...

  • You can use reason-promise directly to write JS bindings.
  • All JS tooling for promises immediately works with reason-promise.
  • Even if you do something exotic, like switch out the promise implementation at the JS level, for, say, better stack traces, reason-promise still binds to it!
<br/>

There is only one exception to the rule that Promise.t maps directly to JS promises: when there is a promise nested inside another promise. JS breaks the type safety of promises in a misguided attempt to disallow nesting. reason-promise instead emulates it in a way that makes promises type-safe again. This is in contrast to BuckleScript's built-in Js.Promise, which directly exposes the JS behavior, and so is not type-safe.

<br/>

In addition:

<br>

Tutorial

<br/>

<a id="Installing"></a>

Installing

npm install reason-promise

Then, add reason-promise to your bsconfig.json:

{
  "bs-dependencies": [
    "reason-promise"
  ]
}
<br/>

<a id="GettingStarted"></a>

Getting started

To quickly get a project for pasting the code examples, clone the [example repo][example-repo]. The code is in main.re.

git clone https://github.com/aantron/promise-example-bsb
cd promise-example-bsb
npm install
npm run test    # To run each example.

There it also an example repo with [a trivial binding to parts of node-fetch][example-binding].

While reading the tutorial, it can be useful to glance at the [type signatures][rei] of the functions from time to time. They provide a neat summary of what each function does and what it expects from its callback.

<br/>

<a id="Creating"></a>

Creating new promises

The most basic function for creating a new promise is [Promise.pending][pending]:

let (p, resolve) = Promise.pending()
Js.log(p)     /* Promise { <pending> } */

The second value returned, resolve, is a function for resolving the promise:

let (p, resolve) = Promise.pending()
resolve("Hello")
Js.log(p)     /* Promise { 'Hello' } */

[Promise.resolved][resolved] is a helper that returns an already-resolved promise:

let p = Promise.resolved("Hello")
Js.log(p)     /* Promise { 'Hello' } */

...and [Promise.exec][exec] is for wrapping functions that take callbacks:

@bs.val external setTimeout: (unit => unit, int) => unit = "setTimeout"

let p = Promise.exec(resolve => setTimeout(resolve, 1000))
Js.log(p)     /* Promise { <pending> } */

/* Program then waits for one second before exiting. */
<br/>

<a id="Values"></a>

Getting values from promises

To do something once a promise is resolved, use [Promise.get][get]:

let (p, resolve) = Promise.pending()

p->Promise.get(s => Js.log(s))

resolve("Hello")    /* Prints "Hello". */
<br/>

<a id="Transforming"></a>

Transforming promises

Use [Promise.map][map] to transform the value inside a promise:

let (p, resolve) = Promise.pending()

p
->Promise.map(s => s ++ " world")
->Promise.get(s => Js.log(s))

resolve("Hello")    /* Hello world */

To be precise, Promise.map creates a new promise with the transformed value.

If the function you are using to transform the value also returns a promise, use [Promise.flatMap][flatMap] instead of Promise.map. Promise.flatMap will flatten the nested promise.

<br/>

<a id="Tracing"></a>

Tracing

If you have a chain of promise operations, and you'd like to inspect the value in the middle of the chain, use [Promise.tap][tap]:

let (p, resolve) = Promise.pending()

p
->Promise.tap(s => Js.log("Value is now: " ++ s))
->Promise.map(s => s ++ " world")
->Promise.tap(s => Js.log("Value is now: " ++ s))
->Promise.get(s => Js.log(s))

resolve("Hello")

/*
Value is now: Hello
Value is now: Hello world
Hello world
*/
<br/>

<a id="Combining"></a>

Concurrent combinations

[Promise.race][race] waits for one of the promises passed to it to resolve:

@bs.val external setTimeout: (unit => unit, int) => unit = "setTimeout"

let one_second = Promise.exec(resolve => setTimeout(resolve, 1000))
let five_seconds = Promise.exec(resolve => setTimeout(resolve, 5000))

Promise.race([one_second, five_seconds])
->Promise.get(() => {
  Js.log("Hello")
  exit(0)
})

/* Prints "Hello" after one second. */

[Promise.all][all] instead waits for all of the promises passed to it, concurrently:

@bs.val external setTimeout: (unit => unit, int) => unit = "setTimeout"

let one_second = Promise.exec(resolve => setTimeout(resolve, 1000))
let five_seconds = Promise.exec(resolve => setTimeout(resolve, 5000))

Promise.all([one_second, five_seconds])
->Promise.get(_ => {
  Js.log("Hello")
  exit(0)
})

/* Prints "Hello" after five seconds. */

For convenience, there are several variants of Promise.all:

  • [Promise.all2][all2]
  • [Promise.all3][all3]
  • [Promise.all4][all4]
  • [Promise.all5][all5]
  • [Promise.all6][all6]
  • [Promise.allArray][allArray]
<br/>

<a id="Errors"></a>

Handling errors with Result

Promises that can fail are represented using the standard library's [Result][Result], and its constructors Ok and Error:

open Belt.Result

Promise.resolved(Ok("Hello"))
->Promise.getOk(s => Js.log(s))       /* Hello */

[Promise.getOk][getOk] waits for p to have a value, and runs its function only if that value is Ok(_). If you instead resolve the promise with Error(_), there will be no output:

open Belt.Result

Promise.resolved(Error("Failed"))
->Promise.getOk(s => Js.log(s))       /* Program just exits. */

You can wait for either kind of value by calling [Promise.getOk][getOk] and [Promise.getError][getError]:

open Belt.Result

let () = {
  let p = Promise.resolved(Error("Failed"))
  p->Promise.getOk(s => Js.log(s))
  p->Promise.getError(s => Js.log("Error: " ++ s))
}                                     /* Error: Failed */

...or respond to all outcomes using the ordinary [Promise.get][get]:

open Belt.Result

Promise.resolved(Error("Failed"))
->Promise.get(result =>
  switch result {
  | Ok(s) => Js.log(s)
  | Error(s) => Js.log("Error: " ++ s)
  })                                  /* Error: Failed */

The full set of functions for handling results is:

  • [Promise.getOk][getOk]
  • [Promise.tapOk][tapOk]
  • [Promise.mapOk][mapOk]
  • [Promise.flatMapOk][flatMapOk]
  • [Promise.getError][getError]
  • [Promise.tapError][tapError]
  • [Promise.mapError][mapError]
  • [Promise.flatMapError][flatMapError]

There are also similar functions for working with [Option][Option]:

  • [Promise.getSome][getSome]
  • [Promise.tapSome][tapSome]
  • [Promise.mapSome][mapSome]
  • [Promise.flatMapSome][flatMapSome]

In addition, there is also a set of variants of Promise.all for results, which propagate any Error(_) as soon as it is received:

  • [Promise.allOk][allOk]
  • [Promise.allOk2][allOk2]
  • [Promise.allOk3][allOk3]
  • [Promise.allOk4][allOk4]
  • [Promise.allOk5][allOk5]
  • [Promise.allOk6][allOk6]
  • [Promise.allOkArray][allOkArray]

If you'd like instead to fully wait for all the promises to resolve with either Ok(_) or Error(_), you can use the ordinary Promise.all and its variants.

<br/>

<a id="Rejection"></a>

Advanced: Rejection

As you can see from Handling errors, Promise doesn't use rejection for errors — but JavaScript promises do. In order to support bindings to JavaScript libraries, which often return promises that can be rejected, Promise provides the [Promise.Js][Promise.Js] helper module.

Promise.Js works the same way as Promise. It similarly has:

  • [Promise.Js.get][Js.get]
  • [Promise.Js.tap][Js.tap]
  • [Promise.Js.map][Js.map]
  • [Promise.Js.flatMap][Js.flatMap]

However, because Promise.Js uses JS rejection for error handling rather than Result or Option,

  • There are no helpers for

Related Skills

View on GitHub
GitHub Stars342
CategoryDevelopment
Updated21d ago
Forks25

Languages

Reason

Security Score

100/100

Audited on Mar 6, 2026

No findings