I have a situation where a library is using reason-promise as a default one branch and not another. I am finding it difficult to switch from one branch to another because I can't, for the life of me, figure out how to use reason-promise. I am not much better at Js.Promise but that is besides the point.
The library in question is reason-apollo-client. The branch has a bunch of other improvements, includes reason-promise
as the default promise
implementation. That branch is the next branch.
By way of example, found at reason-promise-question, I have a Js.Promise
function that gets me the current token value. It has the following type:
unit => Js.Promise.t(
Js.Nullable.t(string)
)
The function can be found in Tokens and is reproduced below. This is a dummy function, there is no binding. The point is to see how to get it to compile.
[@bs.val] [@bs.scope "localStorage"]
external setItem: (string, string) => unit = "setItem";
let setUserToken: string => Js.Promise.t(unit) =
token => Js.Promise.resolve(setItem("auth", token));
[@bs.val] [@bs.scope "localStorage"]
external getItem: string => Js.Nullable.t(string) = "getItem";
let getUserToken: unit => Js.Promise.t(Js.Nullable.t(string)) =
() => Js.Promise.resolve(getItem("auth"));
let setTempUserToken: string => Js.Promise.t(unit) =
_ => Js.Promise.resolve();
let getTempUserToken: unit => Js.Promise.t(Js.Nullable.t(string)) =
() => Js.Promise.resolve(Js.Nullable.undefined);
When I try use this with reason-promise
when creating an apollo/client
authlink
, I get the following error:
unit => Js.Promise.t(
Js.Nullable.t(string)
)
Error: This expression has type
Js.Promise.t(Js.Json.t) = Js.Promise.t(Js.Json.t)
but an expression was expected of type
Promise.t(Js.Json.t) = Promise.rejectable(Js.Json.t, Promise.never)
Here is the authlink
function:
let authLink =
ApolloClient.Link.ContextLink.makeAsync((~operation as _, ~prevContext as ctx) => {
Tokens.getUserToken()
->Js.Promise.then_(
token => {
switch (token->Js.Nullable.toOption) {
| None =>
Tokens.getTempUserToken()
->Js.Promise.then_(
token => Js.Promise.resolve(Js.Nullable.toOption(token)),
_,
)
| Some(token) => Js.Promise.resolve(Some(token))
}
},
_,
)
->Js.Promise.then_(
fun
| None => Js.Promise.resolve(Js.Json.null)
| Some(token) => {
Js.Promise.resolve(
[%raw
{| (context, token) => ({
headers: {
...ctx.headers,
authorization: `Bearer ${token}`
}
}) |}
](
ctx,
token,
),
);
},
_,
)
});
How do we convert this to a reason-promise? Please feel free to be as diadactic as you want to be.
Thank you, in advance.
You should be able to convert your vanilla
Js.Promise.t
into aPromise.t
.First, you will have to convert your
Js.Promise.t
into aPromise.Js.t
. This can be done byThe reason that the extra
Promise.Js.t
type exists is because of the difference in the philosophy of error handling betweenJs.Promise.t
andPromise.t
. The latter, handles errors using theBelt.Result
module (as seen on https://github.com/aantron/promise#handling-errors-with-result) whereas the former uses rejection (which is typically handled withcatch
(in JavaScript) orcatch_
(in Reason)). The intermediate product ofPromise.Js.t
encapsulates the same philosophy as the JavaScript promise.In order to convert the intermediate product to the final
Promise.t
, you will therefore need to explicitly indicate what should happen when/if the intermediate promise gets rejected. This can be done with a few different ways but as an example you can do:This will convert your original
Js.Promise.t('a)
into aPromise.t(Result('a, Js.Promise.error))
.If you'd like to further convert to
Promise.t('a)
, you have to explicitly handle the possible error.See https://github.com/aantron/promise#advanced-rejection for more details.
NB that it looks like apollo has since moved to using vanilla promises (https://github.com/reasonml-community/reason-apollo-client/pull/57).