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 theBelt
,Js
andJs.xxx2
situation is confusing; a proper solution will hopefully be part of our upcomingv11
release.
Creating a promise
RESlet 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
RESlet 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:
RESlet 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
:
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 useJs.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:
RESJs.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:
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)
}, _)