3

I am looking at an example of Reason at A First Reason React app for Javascript developers

And I see that he is calling Js.Promise.resolve when using bs-fetch:

RepoData.fetchRepos()
  |> Js.Promise.then_(repoData => {
       handleReposLoaded(repoData);
       Js.Promise.resolve();
     })
  |> ignore;

I have seen similar code in BuckleScript code as well. For example in Bucklescript Cookbook:

Js.Promise.(
  Fetch.fetch "https://api.github.com/users/reasonml-community/repos"
  |> then_ Fetch.Response.text
  |> then_ (fun text -> 
      text 
      |> names
      |> Array.iter Js.log 
      |> resolve)
  |> ignore

In JS we usually call resolve when we create a new promise, not when using a function that returns a promise. So why do we need to call resolve in the cases above?

glennsl
  • 28,186
  • 12
  • 57
  • 75
599644
  • 561
  • 2
  • 15

1 Answers1

6

Js.Promise.then_ requires that a new promise is returned.

The reason is that es6 promises aren't soundly typed. The value returned in a then callback is dynamically either wrapped or (infinitely) flattened so that it always returns a promise, and never a nested promise. That means if we allow any value to be returned (let then_: ((_ => 'a), Js.Promise.t(_)) => Js.Promise.t('a)), and if that value is a promise ('a = Js.Promise.t('b)), it would have the return type Js.Promise.t(Js.Promise.t('b)), but the value returned will actually have been flattened to just a Js.Promise.t('b).

Having then_ accept only a promise from the callback alleviates this a bit by making it more obvious that you return a nested promise. It's still possible to resolve a promise however, so the type is still not sound, but it makes it a little bit harder to shoot yourself in the foot.

There will be a sound and elegant promise API in the (probably near) future, but since it's a non-trivial task to design and implement it'll take a bit of time to get it right.

glennsl
  • 28,186
  • 12
  • 57
  • 75