From f700b4457e2695208625791deb9503b709f2a8a0 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Wed, 3 Apr 2024 00:31:13 +0900 Subject: [PATCH] Add more bindings to Promise utils (#204) * Add more bindings to Promise utils * more accurate typename * changelog --- CHANGELOG.md | 1 + src/Core__Promise.res | 65 ++++++++++++++++- src/Core__Promise.resi | 154 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 216 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf83e1c..9bacb432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - BREAKING: Intl types: simplify bindings for constructors / functions with optional arguments. https://github.com/rescript-association/rescript-core/pull/198 - Fix: Expose Intl.Common. https://github.com/rescript-association/rescript-core/pull/197 - Add `Array.flatMapWithIndex` https://github.com/rescript-association/rescript-core/pull/199 +- Add `Promise.any`, `Promise.allSettled`, `Promise.withResolvers` https://github.com/rescript-association/rescript-core/pull/204 ## 1.1.0 diff --git a/src/Core__Promise.res b/src/Core__Promise.res index 35b1b898..8a4418d2 100644 --- a/src/Core__Promise.res +++ b/src/Core__Promise.res @@ -3,7 +3,16 @@ type t<+'a> = promise<'a> @new external make: (('a => unit, 'e => unit) => unit) => t<'a> = "Promise" -@val @scope("Promise") +type promiseAndResolvers<'a> = { + promise: t<'a>, + resolve: 'a => unit, + reject: exn => unit, +} + +@scope("Promise") @val +external withResolvers: unit => promiseAndResolvers<_> = "withResolvers" + +@scope("Promise") @val external resolve: 'a => t<'a> = "resolve" @send external then: (t<'a>, 'a => t<'b>) => t<'b> = "then" @@ -34,6 +43,57 @@ external all5: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>)) => t<('a, 'b, 'c, 'd, 'e)> @scope("Promise") @val external all6: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>, t<'f>)) => t<('a, 'b, 'c, 'd, 'e, 'f)> = "all" +@tag("status") +type settledResult<+'a> = + | @as("fulfilled") Fulfilled({value: 'a}) | @as("rejected") Rejected({reason: exn}) + +@scope("Promise") @val +external allSettled: array> => promise>> = "allSettled" + +@scope("Promise") @val +external allSettled2: ((promise<'a0>, promise<'a1>)) => promise<( + settledResult<'a0>, + settledResult<'a1>, +)> = "allSettled" + +@scope("Promise") @val +external allSettled3: ((promise<'a0>, promise<'a1>, promise<'a2>)) => promise<( + settledResult<'a0>, + settledResult<'a1>, + settledResult<'a2>, +)> = "allSettled" + +@scope("Promise") @val +external allSettled4: ((promise<'a0>, promise<'a1>, promise<'a2>, promise<'a3>)) => promise<( + settledResult<'a0>, + settledResult<'a1>, + settledResult<'a2>, + settledResult<'a3>, +)> = "allSettled" + +@scope("Promise") @val +external allSettled5: ( + (promise<'a0>, promise<'a1>, promise<'a2>, promise<'a3>, promise<'a4>) +) => promise<( + settledResult<'a0>, + settledResult<'a1>, + settledResult<'a2>, + settledResult<'a3>, + settledResult<'a4>, +)> = "allSettled" + +@scope("Promise") @val +external allSettled6: ( + (promise<'a0>, promise<'a1>, promise<'a2>, promise<'a3>, promise<'a4>, promise<'a5>) +) => promise<( + settledResult<'a0>, + settledResult<'a1>, + settledResult<'a2>, + settledResult<'a3>, + settledResult<'a4>, + settledResult<'a5>, +)> = "allSettled" + @send external _catch: (t<'a>, exn => t<'a>) => t<'a> = "catch" @@ -46,4 +106,7 @@ let catch = (promise: promise<'a>, callback: exn => promise<'a>): promise<'a> => @scope("Promise") @val external race: array> => t<'a> = "race" +@scope("Promise") @val +external any: array> => t<'a> = "any" + external done: promise<'a> => unit = "%ignore" diff --git a/src/Core__Promise.resi b/src/Core__Promise.resi index 2a366604..5468bf93 100644 --- a/src/Core__Promise.resi +++ b/src/Core__Promise.resi @@ -75,6 +75,37 @@ Promise.make((resolve, reject) => { @new external make: (('a => unit, 'e => unit) => unit) => t<'a> = "Promise" +type promiseAndResolvers<'a> = { + promise: t<'a>, + resolve: 'a => unit, + reject: exn => unit, +} + +/** +`withResolvers()` returns a object containing a new promise with functions to resolve or reject it. See [`Promise.withResolvers`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers) on MDN. + +## Examples + +```rescript +open Promise + +let {promise, resolve, _} = Promise.withResolvers() + +setTimeout(() => { + resolve(. "success") +}, 1000)->ignore + +promise +->thenResolve(str => { + Console.log(str) +}) +->ignore +``` +*/ +@scope("Promise") +@val +external withResolvers: unit => promiseAndResolvers<_> = "withResolvers" + /** `catch(promise, errorCallback)` registers an exception handler in a promise chain. The `errorCallback` receives an `exn` value that can later be refined into a JS @@ -206,7 +237,7 @@ resolve(5) external finally: (t<'a>, unit => unit) => t<'a> = "finally" /** -`race(arr)` combining `array` of promises. See [`Promise.race`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race) on MDN. +`race(arr)` runs all promises concurrently and returns promise settles with the eventual state of the first promise that settles. See [`Promise.race`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race) on MDN. ## Examples @@ -233,8 +264,34 @@ race(promises)->then(winner => { external race: array> => t<'a> = "race" /** -`all(promises)` runs all promises in parallel and returns a new promise resolving -all gathered results in a unified array. See [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) on MDN. +`any(arr)` runs all promises concurrently and returns promise fulfills when any of the input's promises fulfills, with this first fulfillment value. See [`Promise.any`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any) on MDN. + +## Examples + +```rescript +open Promise +let racer = (ms, name) => { + Promise.make((resolve, _) => { + setTimeout(() => { + resolve(name) + }, ms)->ignore + }) +} + +let promises = [racer(1000, "Turtle"), racer(500, "Hare"), racer(100, "Eagle")] + +any(promises)->then(winner => { + Console.log("The winner is " ++ winner) + resolve() +}) +``` +*/ +@scope("Promise") +@val +external any: array> => t<'a> = "any" + +/** +`all(promises)` runs all promises concurrently and returns a promise fulfills when all of the input's promises fulfill, with an array of the fulfillment values. See [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) on MDN. ```rescript open Promise @@ -290,6 +347,97 @@ external all5: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>)) => t<('a, 'b, 'c, 'd, 'e)> @val external all6: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>, t<'f>)) => t<('a, 'b, 'c, 'd, 'e, 'f)> = "all" +@tag("status") +type settledResult<+'a> = + | @as("fulfilled") Fulfilled({value: 'a}) | @as("rejected") Rejected({reason: exn}) + +/** +`allSettled(promises)` runs all promises concurrently and returns promise fulfills when all of the input's promises settle with an array of objects that describe the outcome of each promise. See [`Promise.allSettled`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) on MDN. + +```rescript +open Promise + +exception TestError(string) + +let promises = [resolve(1), resolve(2), reject(TestError("some rejected promise"))] + +allSettled(promises) +->then((results) => { + results->Array.forEach((result) => { + switch result { + | Fulfilled({value: num}) => + Console.log2("Number: ", num) + | Rejected({reason}) => + Console.log(reason) + } + }) + + resolve() +}) +->ignore +``` +*/ +@scope("Promise") +@val +external allSettled: array> => t>> = "allSettled" + +/** +`allSettled2((p1, p2))`. Like `allSettled()`, but with a fixed size tuple of 2 +*/ +@scope("Promise") +@val +external allSettled2: ((t<'a>, t<'b>)) => t<(settledResult<'a>, settledResult<'b>)> = "allSettled" + +/** +`allSettled3((p1, p2, p3))`. Like `allSettled()`, but with a fixed size tuple of 3 +*/ +@scope("Promise") +@val +external allSettled3: ((t<'a>, t<'b>, t<'c>)) => t<( + settledResult<'a>, + settledResult<'b>, + settledResult<'c>, +)> = "allSettled" + +/** +`allSettled4((p1, p2, p3, p4))`. Like `allSettled()`, but with a fixed size tuple of 4 +*/ +@scope("Promise") +@val +external allSettled4: ((t<'a>, t<'b>, t<'c>, t<'d>)) => t<( + settledResult<'a>, + settledResult<'b>, + settledResult<'c>, + settledResult<'d>, +)> = "allSettled" + +/** +`allSettled5((p1, p2, p3, p4, p5))`. Like `allSettled()`, but with a fixed size tuple of 5 +*/ +@scope("Promise") +@val +external allSettled5: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>)) => t<( + settledResult<'a>, + settledResult<'b>, + settledResult<'c>, + settledResult<'d>, + settledResult<'e>, +)> = "allSettled" + +/** +`allSettled6((p1, p2, p4, p5, p6))`. Like `allSettled()`, but with a fixed size tuple of 6 +")*/ +@scope("Promise") +@val +external allSettled6: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>, t<'f>)) => t<( + settledResult<'a>, + settledResult<'b>, + settledResult<'c>, + settledResult<'d>, + settledResult<'e>, + settledResult<'f>, +)> = "allSettled" + /** `done(p)` is a safe way to ignore a promise. If a value is anything else than a promise, it will raise a type error.