Skip to content

Transpiling async/await to promises instead of generators for es5 #31621

@collinsauve

Description

@collinsauve

Search Terms

transpile async promise

Suggestion

When target is ES5, allow an option to transpile async/await to Promises rather than switches. This would require Promises to be polyfilled, hence would be optional.

Old issue for this

This was previously discussed on this issue but was closed with this comment by @mhegazy:

this may be true for code with small list of await expressions. but it does not scale for the general case. once you have a loop with an await expression, you can not use promises.

@mhegazy can you explain this? I have used fast-async to transpile loops to promises before. Is there some edge-case where it is not possible?

Use Cases

Current approach creates emitted code that cannot be source-mapped properly by some debuggers.

Examples

export const foo = async (): Promise<number> => await Promise.resolve(1) + 1;

Currently emits this:

exports.foo = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
    switch (_a.label) {
        case 0: return [4 /*yield*/, Promise.resolve(1)];
        case 1: return [2 /*return*/, (_a.sent()) + 1];
    }
}); }); };

Instead it should emit something like this (output from fast-async:

exports.foo = function foo() {
  return new Promise(function ($return, $error) {
    return Promise.resolve(1).then(function ($await_7) {
      return $return($await_7 + 1);
    }.$asyncbind(this, $error), $error);
  });
};

Loops

Here's an example of fast-async transpiling a loop:

async function Foo() {
    for (let x = 0; x < 5; x++) {
        console.log("FOO: " + await Promise.resolve(x));
    }
}

becomes

function Foo() {
  return new Promise(function ($return, $error) {
    var x;
    x = 0;

    function $Loop_4_step() {
      x++;
      return $Loop_4;
    }

    function $Loop_4() {
      if (x < 5) {
        return Promise.resolve(x).then(function ($await_6) {
          console.log("FOO: " + $await_6);
          return $Loop_4_step;
        }.$asyncbind(this, $error), $error);
      } else return [1];
    }

    return Function.$asyncbind.trampoline(this, $Loop_4_exit, $Loop_4_step, $error, true)($Loop_4);

    function $Loop_4_exit() {
      return $return();
    }
  });
}

Here's a while loop:

async function Foo() {    
    let x = 0;
    while (x < 5) {
        x += await Promise.resolve(1);
        console.log("FOO: " + x);
    }
}

transpiles to:

function Foo() {
  return new Promise(function ($return, $error) {
    var x;
    x = 0;

    function $Loop_4() {
      if (x < 5) {
        return Promise.resolve(1).then(function ($await_6) {
          x += $await_6;
          console.log("FOO: " + x);
          return $Loop_4;
        }.$asyncbind(this, $error), $error);
      } else return [1];
    }

    return Function.$asyncbind.trampoline(this, $Loop_4_exit, $Loop_4, $error, true)($Loop_4);

    function $Loop_4_exit() {
      return $return();
    }
  });
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions