In current prototype of the Async/Await downlevel support, we found a bug causes an erroneous exception to occur in code that should otherwise work.
The gist of the issue is that having a “return” statement inside of a for loop in a function that has an await in it causes an error.
Run this code an you’ll see that whenever the “num” is > .5 (and the “return true” statement gets evaluated), you get the following error that comes from within the __generator code: “TypeError: Unable to get property '0' of undefined or null reference”
async function itemExists(): Q.Promise<boolean> {
var numTries = 3;
for (var i = 0; i < numTries; i++) {
var num = await Q.fcall(function() { return Math.random() });
console.log(num);
if (num > .5) {
return true;
}
}
return false;
}
itemExists().then(function(val) {
console.log("All values greater than .5: " + val)
}).catch(function(e) {
console.error("Error: " + e);
});
The problem is caused by the “return” statement within the loop. If I had modified itemExists() to set a local variable, for example, and then break out of the loop via a “break” statement, the code would work:
// With workaround (NOT short-circuiting the loop)
async function itemExists(): Q.Promise<boolean> {
var numTries = 3;
var result = false;
for (var i = 0; i < numTries; i++) {
var num = await Q.fcall(function() { return Math.random() });
console.log(num);
if (num > .5) {
result = true;
break;
}
}
return result;
}
A quick analysis of the issue seems to be in the generated code. The exception is throw in here:
try {
var operation = body(state);
opcode = operation[0], arg = operation[1];
} catch (e) {
opcode = 1 /*throw*/, arg = e;
}
But the actual culprit seems to be in the fact that there is an inconsistency in the generated return and break statement (so far as I can tell). Namely, 3 looks like sometime a “break” and sometimes a “return”.
function itemExists() {
return new Q.Promise(function (_resolve) {
_resolve(__awaiter(__generator(function (_state) {
switch (_state.label) {
case 0:
numTries = 3;
i = 0;
_state.label = 1;
case 1:
if (!(i < numTries))
return [3 /*break*/, 4];
return [4 /*yield*/, Q.fcall(function () {
return Math.random();
})];
case 2:
num = _state.sent;
console.log(num);
if (num > .5) {
return [3 /*return*/, true];
}
_state.label = 3;
case 3:
i++;
return [3 /*break*/, 1];
case 4:
return [2 /*return*/, false];
}
})));
});
var numTries, i, num;
}
itemExists().then(function (val) {
console.log("All values greater than .5: " + val);
}).catch(function (e) {
console.error("Error: " + e);
});
This state machine looks like a downlevel emit of generators. Does it mean that [email protected] is going to support yield
when targeting es5?
@d180cf we are working on it :)
This should no longer be an issue as of 4685646, which is the new version of these downlevel transformations for async functions we are targeting for our 2.1 release.
Most helpful comment
@d180cf we are working on it :)