title | description | canonical |
---|---|---|
Promises |
JS Promise handling in ReScript |
/docs/manual/v11.0.0/promise |
Note: Starting from ReScript 10.1 and above, we recommend using async / await when interacting with Promises.
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:
// 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 Promise
module.
A builtin module to create, chain and manipulate promises.
let p1 = Promise.make((resolve, reject) => {
// We use uncurried functions for resolve / reject
// for cleaner JS output without unintended curry calls
resolve("hello world")
})
let p2 = Promise.resolve("some value")
// You can only reject `exn` values for streamlined catch handling
exception MyOwnError(string)
let p3 = Promise.reject(MyOwnError("some rejection"))
let logAsyncMessage = () => {
open Promise
Promise.resolve("hello world")
->then(msg => {
// then callbacks require the result to be resolved explicitly
resolve("Message: " ++ msg)
})
->then(msg => {
Console.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:
let logAsyncMessage = async () => {
let msg = await Promise.resolve("hello world")
Console.log(`Message: ${msg}`)
}
Needless to say, the async / await version offers better ergonomics and less opportunities to run into type issues.
You can handle a rejected promise using the Promise.catch()
method, which allows you to catch and manage errors effectively.
In case you want to launch multiple promises in parallel, use Promise.all
:
<CodeTab labels={["ReScript", "JS Output"]}>
@val
external fetchMessage: string => promise<string> = "global.fetchMessage"
let logAsyncMessage = async () => {
let messages = await Promise.all([fetchMessage("message1"), fetchMessage("message2")])
Console.log(messages->Array.joinWith(", "))
}
async function logAsyncMessage(param) {
var messages = await Promise.all([
global.fetchMessage("message1"),
global.fetchMessage("message2")
]);
console.log(messages.join(", "));
}
export {
logAsyncMessage ,
}
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 usePromise
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:
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.
Using the pipe operator:
<CodeTab labels={["ReScript", "JS Output"]}>
let myPromise = Js.Promise.make((~resolve, ~reject) => resolve(. 2))
myPromise->Js.Promise.then_(value => {
Console.log(value)
Js.Promise.resolve(value + 2)
}, _)->Js.Promise.then_(value => {
Console.log(value)
Js.Promise.resolve(value + 3)
}, _)->Js.Promise.catch(err => {
Console.log2("Failure!!", err)
Js.Promise.resolve(-2)
}, _)
var myPromise = new Promise(function (resolve, reject) {
return resolve(2);
});
myPromise
.then(function (value) {
console.log(value);
return Promise.resolve((value + 2) | 0);
})
.then(function (value) {
console.log(value);
return Promise.resolve((value + 3) | 0);
})
.catch(function (err) {
console.log("Failure!!", err);
return Promise.resolve(-2);
});