Typescript: Bug in async awaits in es5

Created on 17 Sep 2020  ·  13Comments  ·  Source: microsoft/TypeScript


TypeScript Version: 4.1.0-dev.20200917


Search Terms: is:issue is:open async label:bug change

Code

async function bar () {
  return {
    a: await Promise.resolve(1),
    b: await Promise.resolve(2),
    c: await Promise.resolve(3),
    d: await Promise.resolve(4),
    e: await Promise.resolve(5),
    f: await Promise.resolve(6),
    g: await Promise.resolve(7),
    h: await Promise.resolve(8),
    // works correctly if you comment the next line
    i: await Promise.resolve(9),
  }
}

bar().then(console.log)

tsconfig.json

{
  "compilerOptions": {
    "lib": ["DOM", "ES2015"]
  }
}

Expected behavior:

{ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9 }

Actual behavior:

{ a: 9, b: 9, c: 9, d: 9, e: 9, f: 9, g: 9, h: 9, i: 9 }

Playground Link:
https://www.typescriptlang.org/play?target=1#code/IYZwngdgxgBAZgV2gFwJYHsIwEbAE4wAUAlDAN4BQMMeApsgnlpddcAFwzADuwqyMAAp50AW1QhaAOjoh0AGwButQgEZiAGiqtsnHnwHCxE6bIXLCAJk3bqUPb35CR4yTNpylKgMw3WMABMHA2djNzMvQgAWP1ZaYKcjV1MPcxUAVljqOATDFxN3TwsANiyYAHNc0OTCtMIAdjKACyqkgoiLAA4y1Fb88NTIgE5YgF8KcYpcPBIpZCbaCEIoTE9peXRy4iA

Related Issues:

40047

Bug Fix Available

Most helpful comment

Yes, I think we can packport the eventual fix to TypeScript 4.0.4. @elibarzilay do you think you'd be able to prioritize this issue?

All 13 comments

The cause seems to be that the generated code use the same value to init all properties (case 9):

// Excluded __awaiter and __generator functions
function bar() {
    return __awaiter(this, void 0, void 0, function () {
        var _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, Promise.resolve(1)];
                case 1: return [4 /*yield*/, Promise.resolve(2)];
                case 2: return [4 /*yield*/, Promise.resolve(3)];
                case 3: return [4 /*yield*/, Promise.resolve(4)];
                case 4: return [4 /*yield*/, Promise.resolve(5)];
                case 5: return [4 /*yield*/, Promise.resolve(6)];
                case 6: return [4 /*yield*/, Promise.resolve(7)];
                case 7: return [4 /*yield*/, Promise.resolve(8)];
                case 8: return [4 /*yield*/, Promise.resolve(9)];
                case 9: return [2 /*return*/, (_a = {},
                        _a.a = _b.sent(),
                        _a.b = _b.sent(),
                        _a.c = _b.sent(),
                        _a.d = _b.sent(),
                        _a.e = _b.sent(),
                        _a.f = _b.sent(),
                        _a.g = _b.sent(),
                        _a.h = _b.sent(),
                        _a.i = _b.sent(),
                        _a)];
            }
        });
    });
}
bar().then(console.log);

At first I thought that this is problem with generator transformations, but looks like version where async changed to generator function and await changed to yield provides correct assignments in each case in compiled JS.

Also original problem is not reproducible in 3.9.7.

The nightly have the same problem.

As a reference for the typescript team, in version 3.9.7 it generates:

// Excluded __awaiter and __generator functions
function bar() {
    return __awaiter(this, void 0, void 0, function () {
        var _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    _a = {};
                    return [4 /*yield*/, Promise.resolve(1)];
                case 1:
                    _a.a = _b.sent();
                    return [4 /*yield*/, Promise.resolve(2)];
                case 2:
                    _a.b = _b.sent();
                    return [4 /*yield*/, Promise.resolve(3)];
                case 3:
                    _a.c = _b.sent();
                    return [4 /*yield*/, Promise.resolve(4)];
                case 4:
                    _a.d = _b.sent();
                    return [4 /*yield*/, Promise.resolve(5)];
                case 5:
                    _a.e = _b.sent();
                    return [4 /*yield*/, Promise.resolve(6)];
                case 6:
                    _a.f = _b.sent();
                    return [4 /*yield*/, Promise.resolve(7)];
                case 7:
                    _a.g = _b.sent();
                    return [4 /*yield*/, Promise.resolve(8)];
                case 8:
                    _a.h = _b.sent();
                    return [4 /*yield*/, Promise.resolve(9)];
                case 9: return [2 /*return*/, (_a.i = _b.sent(),
                        _a)];
            }
        });
    });
}
bar().then(console.log);

Interesting enough @andrewpg found that it doesn't depend on number of await's
but on number of object keys after first await.

Example

Caused by 8231519b853b0fe594cb70b8b89fb8e5c15c2918 in #38880

/cc @elibarzilay @rbuckton

Thanks for the investigation, everyone! 🌟

Would it be possible to backport a fix for this to 4.0.x? We unfortunately had to revert our 4.0.x upgrade because this issue was causing our apps to crash, and we'd love to upgrade to 4.0.x before November :)

Yes, I think we can packport the eventual fix to TypeScript 4.0.4. @elibarzilay do you think you'd be able to prioritize this issue?

Seems like @elibarzilay is having hardware issues. @rbuckton can you take a look at this?

I'm back, kind of. Trying to find it now.

If folks can try out the next nightly version of TypeScript to validate that the fix works for them, we'd appreciate that a ton!

@DanielRosenwasser I tried the nightly out in our repo and it seems to have fixed the issue!

@DanielRosenwasser sorry to bother, but what's the timeline for 4.0.4? We're super eager to upgrade to 4.0.x :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Roam-Cooper picture Roam-Cooper  ·  3Comments

wmaurer picture wmaurer  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comments