Skip to content

yawaramin/prometo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Prometo

npm Build Status

A type-safe promise type for ReasonML, built directly on top of JavaScript but adding fine-grained error control and promise cancellation.

How it works

Prometo is type-safe because of the following:

  • It's not just a 'promise of your data', it's a 'promise of result of your data'. In other words, it can't be affected by JavaScript promises' well-known unsoundness issue where a 'promise of promise of data' is collapsed at runtime to a 'promise of data'.
  • Also because a Prometo promise is a 'promise of result of data', it encodes an error at the type-level using the Reason result('a, 'e) type. In fact, Prometo ensures that its wrapped promises are not rejected, as long as you use its operations. So you can be sure that a Prometo promise is actually not going to throw at runtime. The only point at which you need to care about catching a possible exception is when converting it back into a normal JavaScript promise.

Because it's just a JavaScript promise wrapper, it's also easy to convert back and forth between JavaScript and Prometo promises.

Fine-grained error management

Using the technique described in Composable Error Handling in OCaml, Prometo exposes an error type 'e directly in its main polymorphic promise type Prometo.t('a, 'e). This allows you to explicitly track and manage errors at every point of the code where you use these promises.

Interoperability

It's easy to interop with JavaScript promises:

  • Use fromPromise to convert from a JavaScript Promise to a Prometo promise
  • toPromise to convert from Prometo to a JavaScript promise
  • thenPromise to chain together a Prometo promise and a function that returns a JavaScript Promise, and keep the result as a type-safe Prometo promise.

Cancellation

Prometo promises can be cancelled at any point of usage in the code, using Prometo.cancel. The only caveat is that when you cancel a promise, it doesn't immediately halt whatever is running inside that promise, but lets it run until its result is used by the next flatMap in the promise chain. At that point, the next promise automatically turns into a cancelled promise, stopping whatever was about to happen next from happening.

This also does mean that if you want to cancel a promise chain, you need to ensure that you keep a reference to the first promise in the chain and cancel that.

While not as immediate as something like abortable fetch, in practice this is more general-purpose (it works with any promise, not just ones returned by fetch), and it's enough to prevent errors like calling setState on an unmounted React component. For example:

let example = fetch("https://example.com");
Prometo.forEach(~f=setState, example);

// later...
Prometo.cancel(example); // this will prevent setState from being called

Cancelling a promise too late in the chain won't work:

let result = "https://example.com"
  |> fetch
  |> Prometo.map(~f=setState);

// later...
Prometo.cancel(result); // won't work, too late, setState has already been called

Add to project

  • Add the @yawaramin/prometo project to your package.json (version number in badge at the top of the readme)
  • Add the @yawaramin/prometo dependency to your bs-dependencies list in bsconfig.json
  • Run npx bsb -clean-world, then npx bsb -make-world
  • (Optional but recmomended) if you don't have one already, create a Yawaramin.re (or .ml) file under src/ and alias the Prometo main module there: module Prometo = Yawaramin__Prometo. This lets you access it under the Yawaramin.Prometo namespace throughout your project

API docs

Please go to https://yawaramin.github.io/prometo/