Skip to content

Commit

Permalink
Correctly delegate .return() in async generator (#10422)
Browse files Browse the repository at this point in the history
* Correctly delegate .return() in async generator

* Add catch param

* minNodeVersion

* Add another test
  • Loading branch information
nicolo-ribaudo committed Nov 4, 2019
1 parent 8618447 commit d25262e
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 5 deletions.
6 changes: 5 additions & 1 deletion packages/babel-helpers/src/helpers.js
Expand Up @@ -136,7 +136,7 @@ helpers.AsyncGenerator = helper("7.0.0-beta.0")`
Promise.resolve(wrappedAwait ? value.wrapped : value).then(
function (arg) {
if (wrappedAwait) {
resume("next", arg);
resume(key === "return" ? "return" : "next", arg);
return
}
Expand Down Expand Up @@ -238,6 +238,10 @@ helpers.asyncGeneratorDelegate = helper("7.0.0-beta.0")`
if (typeof inner.return === "function") {
iter.return = function (value) {
if (waiting) {
waiting = false;
return value;
}
return pump("return", value);
};
}
Expand Down
@@ -0,0 +1,20 @@
const log = [];

async function* func1() {
log.push(1);
yield "a";
log.push(2);
}

async function* func2() {
yield* func1();
log.push(3);
}

return (async () => {
const iterator = func2();
await iterator.next();
await iterator.return();

expect(log).toEqual([1]);
})();
@@ -0,0 +1,3 @@
{
"minNodeVersion": "8.0.0"
}
@@ -0,0 +1,10 @@
{
"parserOpts": {
"allowReturnOutsideFunction": true
},
"plugins": [
"external-helpers",
"transform-async-to-generator",
"proposal-async-generator-functions"
]
}
@@ -0,0 +1,34 @@
const log = [];

async function* inner() {
try {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
} finally {
log.push(4);
yield "c";
log.push(5);
}
}

async function* outer() {
log.push(6);
yield* inner();
log.push(7);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([6, 1]);

const [res1, res2] = await Promise.all([ iterator.return("x"), iterator.return("y") ]);
expect(res1).toEqual({ value: "c", done: false });
expect(res2).toEqual({ value: "y", done: true });
expect(log).toEqual([6, 1, 4]);
})();
@@ -0,0 +1,3 @@
{
"minNodeVersion": "8.0.0"
}
@@ -0,0 +1,37 @@
const log = [];

async function* inner() {
try {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
} finally {
log.push(4);
yield "c";
log.push(5);
}
}

async function* outer() {
log.push(6);
yield* inner();
log.push(7);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([6, 1]);

res = await iterator.return("x");
expect(res).toEqual({ value: "c", done: false });
expect(log).toEqual([6, 1, 4]);

res = await iterator.return("y");
expect(res).toEqual({ value: "y", done: true });
expect(log).toEqual([6, 1, 4]);
})();
@@ -0,0 +1,3 @@
{
"minNodeVersion": "8.0.0"
}
@@ -0,0 +1,37 @@
const log = [];

async function* inner() {
try {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
} finally {
log.push(4);
yield "c";
log.push(5);
}
}

async function* outer() {
log.push(6);
yield* inner();
log.push(7);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([6, 1]);

res = await iterator.return();
expect(res).toEqual({ value: "c", done: false });
expect(log).toEqual([6, 1, 4]);

res = await iterator.next();
expect(res).toEqual({ value: undefined, done: true });
expect(log).toEqual([6, 1, 4, 5, 7]);
})();
@@ -0,0 +1,31 @@
const log = [];

async function* inner() {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
}

async function* outer() {
log.push(4);
yield* inner();
log.push(5);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([4, 1]);

res = await iterator.return();
expect(res).toEqual({ value: undefined, done: true });
expect(log).toEqual([4, 1]);

res = await iterator.next();
expect(res).toEqual({ value: undefined, done: true });
expect(log).toEqual([4, 1]);
})();
@@ -0,0 +1,3 @@
{
"minNodeVersion": "8.0.0"
}
@@ -0,0 +1,37 @@
const log = [];

async function* inner() {
try {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
} catch (e) {
log.push(4);
yield "c";
log.push(5);
}
}

async function* outer() {
log.push(6);
yield* inner();
log.push(7);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([6, 1]);

res = await iterator.throw(new Error("TEST"));
expect(res).toEqual({ value: "c", done: false });
expect(log).toEqual([6, 1, 4]);

res = await iterator.next();
expect(res).toEqual({ value: undefined, done: true });
expect(log).toEqual([6, 1, 4, 5, 7]);
})();
@@ -0,0 +1,37 @@
const log = [];

async function* inner() {
try {
log.push(1);
yield "a";
log.push(2);
yield "b";
log.push(3);
} finally {
log.push(4);
yield "c";
log.push(5);
}
}

async function* outer() {
log.push(6);
yield* inner();
log.push(7);
}

return (async () => {
const iterator = outer();

let res = await iterator.next();
expect(res).toEqual({ value: "a", done: false });
expect(log).toEqual([6, 1]);

res = await iterator.throw(new Error("TEST"));
expect(res).toEqual({ value: "c", done: false });
expect(log).toEqual([6, 1, 4]);

// "yield" in finally suspended the exception for one turn
await expect(iterator.next()).rejects.toThrow(/TEST/);
expect(log).toEqual([6, 1, 4, 5]);
})();
Expand Up @@ -4,7 +4,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; 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; } }
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(key === "return" ? "return" : "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; }; }

Expand Down
Expand Up @@ -310,7 +310,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; 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; } }
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(key === "return" ? "return" : "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; }; }

Expand Down
Expand Up @@ -42,7 +42,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; 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; } }
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(key === "return" ? "return" : "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; }; }

Expand Down
Expand Up @@ -12,7 +12,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; 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; } }
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(key === "return" ? "return" : "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; }; }

Expand Down

0 comments on commit d25262e

Please sign in to comment.