Skip to content

Files

Latest commit

 

History

History
182 lines (134 loc) · 4.75 KB

promise.mdx

File metadata and controls

182 lines (134 loc) · 4.75 KB
title description canonical
Promises
JS Promise handling in ReScript
/docs/manual/v11.0.0/promise

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:

// 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.

Promise

A builtin module to create, chain and manipulate promises.

Creating a promise

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"))

Access the contents and transform a promise

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.

Handling Rejected Promises

You can handle a rejected promise using the Promise.catch() method, which allows you to catch and manage errors effectively.

Run multiple promises in parallel

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 ,
}

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 Promise 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.

Usage

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);
  });