MatAtBread / fast-async

605 stars 21 forks source link

Add support for alternative Promise implementations #63

Closed kj800x closed 5 years ago

kj800x commented 5 years ago

Problem Statement

We would love to use this plugin, but we cannot use native promises due to the fact that there isn't yet a standard cross-browser solution for reporting unhandled promise rejections. For this reason, our team is using Q.Promise instead of native Promise. We would like fast-async to use a configurable implementation of Promise, so that we can configure it to use Q.Promise instead of native promises.

We have attempted a solution similar to the "Promises polyfill" section of the README using webpack.DefinePlugin to rewrite Promise, but it does not support our case, as we cannot universally rewrite usages of Promise to Q.Promise in the codebase. Some of our other plugins generate native promises which we do not want to change.

Proposed Solution

There is a new configuration option, which when set causes fast-async to use it's value instead of Promise in generated code.

Proposed Config

{
  "plugins": [
    [
      "module:fast-async",
      {
        "compiler": {
          "spec": true,
          "promiseImplementation": "Q.Promise"
        }
      }
    ]
  ]
}

Sample Input

export const sleep = ms =>
  new Promise(resolve => {
    setTimeout(resolve, ms);
  });

export async function foo(e) {
  const value = await e;
  if (value < 10) {
    return 1;
  } else {
    await sleep(1000);
    return 100;
  }
}

Sample Output

Note that the Promise inside of sleep should not use Q.Promise, since it was explicitly written to use native Promise, but the Promises generated by fast-async should use Q.Promise

var sleep = function sleep(ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms);
  });
};
function foo(e) {
  return new Q.Promise(function ($return, $error) {
    var value;
    return Q.Promise.resolve(e).then(function ($await_2) {
      try {
        value = $await_2;

        if (value < 10) {
          return $return(1);
        } else {
          return Q.Promise.resolve(sleep(1000)).then(function ($await_3) {
            try {
              return $return(100);
            } catch ($boundEx) {
              return $error($boundEx);
            }
          }, $error);
        }

        return $return();
      } catch ($boundEx) {
        return $error($boundEx);
      }
    }.bind(this), $error);
  });
}
kj800x commented 5 years ago

I just started digging into nodent-compiler to see how hard this would be to PR... There's an undocumented option $Promise which does exactly what we need!

https://github.com/MatAtBread/nodent-compiler/blob/03c49cf/compiler.js#L269