Promise

Note: Starting from ReScript 10.1 and above, we recommend using async / await when interacting with Promises.

promise type

Since 10.1

In ReScript, every JS promise is represented with the globally available promise<'a> type. For ReScript versions < 10.1, use its original alias Js.Promise.t<'a> instead.

Here's a usage example in a function signature:

RESI
// User.resi file type user = {name: string} let fetchUser: string => promise<user>

To work with promise values (instead of using async / await) you may want to use the built in Js.Promise2 module.

Js.Promise2

A builtin module to create, chain and manipulate promises.

Note: This is an intermediate replacement for the Js.Promise module. It is designed to work with the -> operator and should be used in favour of it's legacy counterpart. We are aware that the Belt, Js and Js.xxx2 situation is confusing; a proper solution will hopefully be part of our upcoming v11 release.

Creating a promise

RES
let p1 = Js.Promise2.make((~resolve, ~reject) => { // We use uncurried functions for resolve / reject // for cleaner JS output without unintended curry calls resolve(. "hello world") }) let p2 = Js.Promise2.resolve("some value") // You can only reject `exn` values for streamlined catch handling exception MyOwnError(string) let p3 = Js.Promise2.reject(MyOwnError("some rejection"))

Access the contents and transform a promise

RES
let logAsyncMessage = () => { open Js.Promise2 Js.Promise2.resolve("hello world") ->then(msg => { // then callbacks require the result to be resolved explicitly resolve("Message: " ++ msg) }) ->then(msg => { Js.log(msg) // Even if there is no result, we need to use resolve() to return a promise resolve() }) ->ignore // Requires ignoring due to unhandled return value }

For comparison, the async / await version of the same code would look like this:

RES
let logAsyncMessage = async () => { let msg = await Js.Promise2.resolve("hello world") Js.log(`Message: ${msg}`) }

Needless to say, the async / await version offers better ergonomics and less opportunities to run into type issues.

Run multiple promises in parallel

In case you want to launch multiple promises in parallel, use Js.Promise2.all:

ReScriptJS Output
@val
external fetchMessage: string => promise<string> = "global.fetchMessage"

let logAsyncMessage = async () => {
  let messages = await Js.Promise2.all([fetchMessage("message1"), fetchMessage("message2")])

  Js.log(Js.Array2.joinWith(messages, ", "))
}

Js.Promise module (legacy - do not use)

Note: The Js.Promise bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility. Either use Js.Promise2 or a third-party promise binding instead.

ReScript has built-in support for JavaScript promises. The 3 functions you generally need are:

  • Js.Promise.resolve: 'a => Js.Promise.t('a)

  • Js.Promise.then_: ('a => Js.Promise.t('b), Js.Promise.t('a)) => Js.Promise.t('b)

  • Js.Promise.catch: (Js.Promise.error => Js.Promise.t('a), Js.Promise.t('a)) => Js.Promise.t('a)

Additionally, here's the type signature for creating a promise on the ReScript side:

RES
Js.Promise.make: ( ( ~resolve: (. 'a) => unit, ~reject: (. exn) => unit ) => unit ) => Js.Promise.t<'a>

This type signature means that make takes a callback that takes 2 named arguments, resolve and reject. Both arguments are themselves uncurried callbacks (with a dot). make returns the created promise.

Usage

Using the pipe operator:

ReScriptJS Output
let myPromise = Js.Promise.make((~resolve, ~reject) => resolve(. 2))

myPromise->Js.Promise.then_(value => {
  Js.log(value)
  Js.Promise.resolve(value + 2)
}, _)->Js.Promise.then_(value => {
  Js.log(value)
  Js.Promise.resolve(value + 3)
}, _)->Js.Promise.catch(err => {
  Js.log2("Failure!!", err)
  Js.Promise.resolve(-2)
}, _)