Adhering to async generator yield behavior change (#6452)

This commit is contained in:
Mateusz Burzyński
2017-10-21 21:48:27 +02:00
committed by GitHub
parent 39d05da3ed
commit 4684edaec7
14 changed files with 86 additions and 33 deletions

View File

@@ -5,7 +5,7 @@ const awaitTemplate = `
function* wrapper() {
var ITERATOR_COMPLETION = true;
var ITERATOR_HAD_ERROR_KEY = false;
var ITERATOR_ERROR_KEY = undefined;
var ITERATOR_ERROR_KEY;
try {
for (
var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY, STEP_VALUE;
@@ -38,7 +38,7 @@ const buildForAwaitWithoutWrapping = template(
awaitTemplate.replace(/\bAWAIT\b/g, ""),
);
export default function(path, helpers) {
export default function(path, { getAsyncIterator, wrapAwait }) {
const { node, scope, parent } = path;
const stepKey = scope.generateUidIdentifier("step");
@@ -58,9 +58,7 @@ export default function(path, helpers) {
]);
}
const build = helpers.wrapAwait
? buildForAwait
: buildForAwaitWithoutWrapping;
const build = wrapAwait ? buildForAwait : buildForAwaitWithoutWrapping;
let template = build({
ITERATOR_HAD_ERROR_KEY: scope.generateUidIdentifier("didIteratorError"),
ITERATOR_COMPLETION: scope.generateUidIdentifier(
@@ -68,11 +66,11 @@ export default function(path, helpers) {
),
ITERATOR_ERROR_KEY: scope.generateUidIdentifier("iteratorError"),
ITERATOR_KEY: scope.generateUidIdentifier("iterator"),
GET_ITERATOR: helpers.getAsyncIterator,
GET_ITERATOR: getAsyncIterator,
OBJECT: node.right,
STEP_VALUE: stepValue,
STEP_KEY: stepKey,
...(helpers.wrapAwait ? { AWAIT: helpers.wrapAwait } : {}),
...(wrapAwait ? { AWAIT: wrapAwait } : {}),
});
// remove generator function wrapper

View File

@@ -10,11 +10,21 @@ const awaitVisitor = {
path.skip();
},
AwaitExpression({ node }, { wrapAwait }) {
node.type = "YieldExpression";
if (wrapAwait) {
node.argument = t.callExpression(wrapAwait, [node.argument]);
AwaitExpression(path, { wrapAwait }) {
const argument = path.get("argument");
if (path.parentPath.isYieldExpression()) {
path.replaceWith(argument.node);
return;
}
path.replaceWith(
t.yieldExpression(
wrapAwait
? t.callExpression(wrapAwait, [argument.node])
: argument.node,
),
);
},
ForOfStatement(path, { file, wrapAwait }) {

View File

@@ -89,7 +89,7 @@ helpers.asyncIterator = defineHelper(`
helpers.AwaitValue = defineHelper(`
export default function _AwaitValue(value) {
this.value = value;
this.wrapped = value;
}
`);
@@ -106,7 +106,7 @@ helpers.AsyncGenerator = defineHelper(`
arg: arg,
resolve: resolve,
reject: reject,
next: null
next: null,
};
if (back) {
@@ -122,13 +122,18 @@ helpers.AsyncGenerator = defineHelper(`
try {
var result = gen[key](arg)
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(
function (arg) { resume("next", arg); },
function (arg) { resume("throw", arg); });
} else {
settle(result.done ? "return" : "normal", result.value);
}
var wrappedAwait = value instanceof AwaitValue;
Promise.resolve(wrappedAwait ? value.wrapped : value).then(
function (arg) {
if (wrappedAwait) {
resume("next", arg);
return
}
settle(result.done ? "return" : "normal", arg);
},
function (err) { resume("throw", err); });
} catch (err) {
settle("throw", err);
}

View File

@@ -0,0 +1,24 @@
"use strict";
const actual = [];
const expected = ["foo", "bar", "baz", "xyz"];
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function* test() {
yield await delay(0).then(() => actual.push("foo"));
await delay(0).then(() => actual.push("bar"));
yield delay(0).then(() => actual.push("baz"));
actual.push("xyz");
}
async function main() {
const g = test();
g.next();
g.next();
await g.next();
}
return main().then(() => {
assert.deepEqual(actual, expected);
});

View File

@@ -0,0 +1,11 @@
{
"plugins": [
"external-helpers",
"transform-async-to-generator",
"transform-async-generator-functions"
],
"presets": ["es2015"],
"parserOpts": {
"allowReturnOutsideFunction": true
}
}

View File

@@ -1,7 +1,8 @@
babelHelpers.asyncToGenerator(function* () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {

View File

@@ -2,7 +2,8 @@ let f = (() => {
var _ref = babelHelpers.asyncToGenerator(function* () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {

View File

@@ -2,7 +2,8 @@ let g = (() => {
var _ref = babelHelpers.wrapAsyncGenerator(function* () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(y), _step, _value; _step = yield babelHelpers.awaitAsyncGenerator(_iterator.next()), _iteratorNormalCompletion = _step.done, _value = yield babelHelpers.awaitAsyncGenerator(_step.value), !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {

View File

@@ -2,7 +2,8 @@ let f = (() => {
var _ref = babelHelpers.asyncToGenerator(function* () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(a), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {

View File

@@ -1,7 +1,8 @@
babelHelpers.asyncToGenerator(function* () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(iterable), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) {

View File

@@ -16,7 +16,7 @@ function _awaitAsyncGenerator(value) { return new _AwaitValue(value); }
function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerator(fn.apply(this, arguments)); }; }
function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof _AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } }
function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; var wrappedAwait = value instanceof _AwaitValue; Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) { if (wrappedAwait) { resume("next", arg); return; } settle(result.done ? "return" : "normal", arg); }, function (err) { resume("throw", err); }); } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } }
if (typeof Symbol === "function" && Symbol.asyncIterator) { _AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; }
@@ -26,4 +26,4 @@ _AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw",
_AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); };
function _AwaitValue(value) { this.value = value; }
function _AwaitValue(value) { this.wrapped = value; }