Skip to content

Repository files navigation

npmBuild StatusCoverage Status

The Future is Now

Future is a lightweight, functional alternative to Js.Promise for ReScript, designed to make async code more composable and maintainable.

Compatibility

  • Use reason-future@2.6.0 for ReasonML or ReScript ≤ v10.
  • Use reason-future@3.0.0 for ReScript ≥ v11.

Key Benefits of Using Future Over Promises:

  • Lighter weight – Only ~25 lines of implementation.
  • Simpler – Futures only resolve to a single type (as opposed to resolve and reject types), giving you more flexibility on your error handling.
  • More robust – Futures have sound typing (unlike JS promises).

Installation

First make sure you have rescript >= 11.1.X. Then install with npm:

$ npm install --save reason-future

Then add "reason-future" to your rescript.json dev dependencies:

{
...
"bs-dependencies": [
"reason-future"
]
}

Basic Usage

To create a task, use Future.make. It provides a single resolve function, similar to how Promises work but without a reject:

letfutureGreeting=Future.make(resolve=>resolve("hi"));

To get the value of a future, use Future.get:

letfutureGreeting=Future.make(resolve=>resolve("hi"));futureGreeting->Future.get(x=>Js.log("Got value: "++x));/* Alternatively: */Future.make(resolve=>resolve("hi"))->Future.get(x=>Js.log("Got value: "++x));

Future.get only retrieves the future value. If you want to transform it to a different value, then you should use Future.map:

/* Shortcut for: let future_A = Future.make(resolve => resolve(99)); */letfuture_A=Future.value(99);letfuture_B=future_A->Future.map(n=>n+1);future_A->Future.get(n=>Js.log(n));/* logs: 99 */future_B->Future.get(n=>Js.log(n));/* logs: 100 */

And finally, if you map a future and return another future, you probably want to flatMap instead:

letfutureNum=Future.value(50);letft_a=futureNum->Future.map(n=>Future.value(n+10));letft_b=futureNum->Future.flatMap(n=>Future.value(n+20));/* ft_a has type future(future(int)) – probably not what you want. *//* ft_b has type future(int) */

API

Core functions. Note:_ represents the future itself as inserted by -> (the pipe operator).

  • Future.make(resolver) - Create a new, potentially-async future.
  • Future.value(x) - Create a new future with a plain value (synchronous).
  • Future.map(_,fn) - Transform a future value into another value
  • Future.flatMap(_,fn) - Transform a future value into another future value
  • Future.get(_,fn) - Get the value of a future
  • Future.tap(_,fn) - Do something with the value of a future without changing it. Returns the same future so you can continue using it in a pipeline. Convenient for side effects such as console logging.
  • Future.all(_,fn) - Turn a list of futures into a future of a list. Used when you want to wait for a collection of futures to complete before doing something (equivalent to Promise.all in Javascript).

Result

Convenience functions when working with a future Result. Note:_ represents the future itself as inserted by -> (the pipe operator).

Note 2: The terms Result.Ok and Result.Error in this context are expected to be read as Ok and Error.

  • Future.mapOk(_,fn) - Transform a future value into another value, but only if the value is an Result.Ok. Similar to Promise.prototype.then
  • Future.mapError(_,fn) - Transform a future value into another value, but only if the value is an Result.Error. Similar to Promise.prototype.catch
  • Future.tapOk(_,fn) - Do something with the value of a future without changing it, but only if the value is a Ok. Returns the same future. Convenience for side effects such as console logging.
  • Future.tapError(_,fn) - Same as tapOk but for Result.Error

The following are more situational:

  • Future.flatMapOk(_, fn) - Transform a future Result.Ok into a future Result. Flattens the inner future.
  • Future.flatMapError(_, fn) - Transform a future Result.Error into a future Result. Flattens the inner future.

FutureJs

Convenience functions for interop with JavaScript land.

  • FutureJs.fromPromise(promise, errorTransformer)
    • promise is the RescriptCore.Promise.t('a) that will be transformed into a Future.t(result('a, 'e))
    • errorTransformer allows you to determine how Promise.error objects will be transformed before they are returned wrapped within a Error. This allows you to implement the error handling method which best meets your application's needs.
  • FutureJs.toPromise(future)
    • future is any Future.t('a) which is transformed into a RescriptCore.Promise.t('a). Always resolves, never rejects the promise.
  • FutureJs.resultToPromise(future)
    • future is the Future.t(result('a, 'e)) which is transformed into a RescriptCore.Promise.t('a). Resolves the promise on Ok result and rejects on Error result.

Example use:

/* This error handler is super simple; you will probably want to write something more sophisticated in your app.*/lethandleError=Js.String.make;somePromiseGetter()->FutureJs.fromPromise(handleError)->Future.tap(value=>Js.log2("It worked!",value))->Future.tapError(err=>Js.log2("uh on",err));

See Composible Error Handling in OCaml for several strategies that you may employ.

Stack Safety

By default this library is not stack safe, you will recieve a 'Maximum call stack size exceeded' error if you recurse too deeply. You can opt into stack safety by passing an optional parameter to the constructors of trampoline. This has a slight overhead. For example:

letstackSafe=Future.make(~executor=`trampoline, resolver);letstackSafeValue=Future.value(~executor=`trampoline,"Value");

TODO

Build

npm run build

Build + Watch

npm run start

Test

npm test

Editor

If you use vscode, Press Windows + Shift + B it will build automatically

About

A Js.Promise alternative for ReScript

Resources

Stars

216 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages