Promise performance


#1

Hello,

I’m building a CLI API client using reason, and I’m using the only bindings that are available for making http request, bs-axios.

It uses promises heavily, and it is fine. The openings and all that stuff make working with promises more or less comfortable.
The type system however gets in the way from time to time, forcing you to always return a value wrapped in a promise. This is reasonable within reason because at the end the type system is what makes it cool so I can live with it. However, inspecting the generated JS code I can see that it uses lots of Promise.resolve to wrap the returned values. This is totally unnecessary because JS promises do it anyway, and I’m a bit worried about performance.

Take a look at this simple code:

let upgrade = (~stack, ~image, client) => {
  open Js.Promise;
  Js.log("Upgrading " ++ stack ++ " using " ++ image ++ " image");
  getStacks(client)
  |> then_(x =>
       x##data
       |> findStack(~name=stack)
       |> Belt.Option.getExn
       |> (st => st##links##services |> Instance.get(client.axios))
     )
  |> then_(x => Belt.Result.Ok (x##data##data) |> resolve)
  |> catch( _err => Belt.Result.Error("Can not find stack " ++ stack ) |> resolve)
};

I have to add some resolve calls because I can’t just return random things within a then, I can live with that, however as I said, the generated code seems unnecessary verbose about this:

function upgrade(stack, image, client) {
  console.log("Upgrading " + (stack + (" using " + (image + " image"))));
  return getStacks(client).then((function (x) {
                    var st = Belt_Option.getExn(findStack(stack)(x.data));
                    return client[/* axios */0].get(st.links.services);
                  })).then((function (x) {
                  return Promise.resolve(/* Ok */Block.__(0, [x.data.data]));
                })).catch((function () {
                return Promise.resolve(/* Error */Block.__(1, ["Can not find stack " + stack]));
              }));

There are two Promise.resolve that are totally redundant.

This does not happen to all my functions tough, here is an example where I’m returning some random value within a then callback and I am not being forced to wrap it on a resolve

let getStacks = client =>
  Js.Promise.(
    Instance.get(client.axios, "v2-beta/projects?name=" ++ client.env)
    |> then_(x =>
         x##data##data[0]##links##stacks |> Instance.get(client.axios)
       )
    |> then_(x => x##data) /* This is a random value, not a promise*/
  );

The generated code looks normal:

function getStacks(client) {
  return client[/* axios */0].get("v2-beta/projects?name=" + client[/* env */1]).then((function (x) {
                  return client[/* axios */0].get(Caml_array.caml_array_get(x.data.data, 0).links.stacks);
                })).then((function (x) {
                return x.data;
              }));
}

Apart from the functions returning promises, why do some values require a resolve call while others don’t ? What makes the promise.resolve to appear on the output code ?

Regards


#2

(For other readers) We ended up discussing this in a BuckleScript issue, here’s the link: https://github.com/BuckleScript/bucklescript/issues/3065#issuecomment-425869818