Skip to content

Commit

Permalink
Await promises from sync iterators with for-await (#13824)
Browse files Browse the repository at this point in the history
Co-authored-by: Mickey Rose <lightmare@users.noreply.github.com>
  • Loading branch information
nicolo-ribaudo and lightmare committed Oct 7, 2021
1 parent 6029252 commit b9ea196
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 41 deletions.
8 changes: 8 additions & 0 deletions packages/babel-helpers/src/helpers-generated.ts
Expand Up @@ -5,6 +5,14 @@

import template from "@babel/template";

export const asyncIterator = {
minVersion: "7.15.9",
ast: () =>
template.program.ast(
'\nexport default function _asyncIterator(iterable) {\n var method, async, sync;\n if (typeof Symbol !== "undefined") {\n async = Symbol.asyncIterator;\n sync = Symbol.iterator;\n }\n do {\n if (!sync) {\n async = "@@asyncIterator";\n sync = "@@iterator";\n }\n if (async && (method = iterable[async]) != null) {\n return method.call(iterable);\n }\n if ((method = iterable[sync]) != null) {\n return new AsyncFromSyncIterator(method.call(iterable));\n }\n } while (!(sync === "@@iterator" || (sync = null)));\n throw new TypeError("Object is not async iterable");\n}\nfunction AsyncFromSyncIterator(s) {\n AsyncFromSyncIterator = function (s) {\n this.s = s;\n this.n = s.next;\n };\n AsyncFromSyncIterator.prototype = {\n s: null,\n n: null,\n next: function () {\n return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments));\n },\n return: function (value) {\n var ret = this.s.return;\n if (ret === undefined) {\n return Promise.resolve({ value: value, done: true });\n }\n return AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments));\n },\n throw: function (value) {\n var thr = this.s.return;\n if (thr === undefined) return Promise.reject(value);\n return AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments));\n },\n };\n function AsyncFromSyncIteratorContinuation(r) {\n \n if (Object(r) !== r) {\n return Promise.reject(new TypeError(r + " is not an object."));\n }\n var done = r.done;\n return Promise.resolve(r.value).then(function (value) {\n return { value: value, done: done };\n });\n }\n return new AsyncFromSyncIterator(s);\n}\n',
),
};

export const jsx = {
minVersion: "7.0.0-beta.0",
ast: () =>
Expand Down
14 changes: 0 additions & 14 deletions packages/babel-helpers/src/helpers.ts
Expand Up @@ -16,20 +16,6 @@ const helper = (minVersion: string) => (tpl: TemplateStringsArray) => ({
ast: () => template.program.ast(tpl),
});

helpers.asyncIterator = helper("7.0.0-beta.0")`
export default function _asyncIterator(iterable) {
var method;
if (typeof Symbol !== "undefined") {
if (Symbol.asyncIterator) method = iterable[Symbol.asyncIterator];
if (method == null && Symbol.iterator) method = iterable[Symbol.iterator];
}
if (method == null) method = iterable["@@asyncIterator"];
if (method == null) method = iterable["@@iterator"]
if (method == null) throw new TypeError("Object is not async iterable");
return method.call(iterable);
}
`;

helpers.AwaitValue = helper("7.0.0-beta.0")`
export default function _AwaitValue(value) {
this.wrapped = value;
Expand Down
67 changes: 67 additions & 0 deletions packages/babel-helpers/src/helpers/asyncIterator.js
@@ -0,0 +1,67 @@
/* @minVersion 7.15.9 */

export default function _asyncIterator(iterable) {
var method,
async,
sync,
retry = 2;

if (typeof Symbol !== "undefined") {
async = Symbol.asyncIterator;
sync = Symbol.iterator;
}

while (retry--) {
if (async && (method = iterable[async]) != null) {
return method.call(iterable);
}
if (sync && (method = iterable[sync]) != null) {
return new AsyncFromSyncIterator(method.call(iterable));
}

async = "@@asyncIterator";
sync = "@@iterator";
}

throw new TypeError("Object is not async iterable");
}

function AsyncFromSyncIterator(s) {
AsyncFromSyncIterator = function (s) {
this.s = s;
this.n = s.next;
};
AsyncFromSyncIterator.prototype = {
/* SyncIterator */ s: null,
/* SyncIterator.[[Next]] */ n: null,
next: function () {
return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments));
},
return: function (value) {
var ret = this.s.return;
if (ret === undefined) {
return Promise.resolve({ value: value, done: true });
}
return AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments));
},
throw: function (value) {
var thr = this.s.return;
if (thr === undefined) return Promise.reject(value);
return AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments));
},
};

function AsyncFromSyncIteratorContinuation(r) {
// This step is _before_ calling AsyncFromSyncIteratorContinuation in the spec.
if (Object(r) !== r) {
return Promise.reject(new TypeError(r + " is not an object."));
}

var done = r.done;
return Promise.resolve(r.value).then(function (value) {
return { value: value, done: done };
});
}

return new AsyncFromSyncIterator(s);
}
@@ -0,0 +1,12 @@
async function* fn() {
for await (const result of [Promise.resolve("ok")]) {
return { result };
}
}

return fn().next().then(result => {
expect(result).toEqual({
done: true,
value: { result: "ok" }
});
});
@@ -0,0 +1,5 @@
async function* fn() {
for await (const result of [Promise.resolve("ok")]) {
return { result };
}
}
@@ -0,0 +1,6 @@
{
"plugins": ["proposal-async-generator-functions"],
"parserOpts": {
"allowReturnOutsideFunction": true
}
}
@@ -0,0 +1,35 @@
function fn() {
return _fn.apply(this, arguments);
}

function _fn() {
_fn = babelHelpers.wrapAsyncGenerator(function* () {
var _iteratorAbruptCompletion = false;
var _didIteratorError = false;

var _iteratorError;

try {
for (var _iterator = babelHelpers.asyncIterator([Promise.resolve("ok")]), _step; _iteratorAbruptCompletion = !(_step = yield babelHelpers.awaitAsyncGenerator(_iterator.next())).done; _iteratorAbruptCompletion = false) {
const result = _step.value;
return {
result
};
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (_iteratorAbruptCompletion && _iterator.return != null) {
yield babelHelpers.awaitAsyncGenerator(_iterator.return());
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
});
return _fn.apply(this, arguments);
}
@@ -0,0 +1,7 @@
async function* fn() {
yield* [Promise.resolve("ok")] // CreateAsyncFromSyncIterator
}

return fn().next().then(result => {
expect(result).toEqual({ value: "ok", done: false });
});
@@ -0,0 +1,3 @@
async function* fn() {
yield* [Promise.resolve("ok")] // CreateAsyncFromSyncIterator
}
@@ -0,0 +1,6 @@
{
"plugins": ["proposal-async-generator-functions"],
"parserOpts": {
"allowReturnOutsideFunction": true
}
}
@@ -0,0 +1,10 @@
function fn() {
return _fn.apply(this, arguments);
}

function _fn() {
_fn = babelHelpers.wrapAsyncGenerator(function* () {
yield* babelHelpers.asyncGeneratorDelegate(babelHelpers.asyncIterator([Promise.resolve("ok")]), babelHelpers.awaitAsyncGenerator); // CreateAsyncFromSyncIterator
});
return _fn.apply(this, arguments);
}
18 changes: 9 additions & 9 deletions packages/babel-runtime-corejs2/package.json
Expand Up @@ -18,6 +18,15 @@
"regenerator-runtime": "^0.13.4"
},
"exports": {
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/jsx": [
{
"node": "./helpers/jsx.js",
Expand Down Expand Up @@ -54,15 +63,6 @@
"./helpers/wrapRegExp.js"
],
"./helpers/esm/wrapRegExp": "./helpers/esm/wrapRegExp.js",
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/AwaitValue": [
{
"node": "./helpers/AwaitValue.js",
Expand Down
18 changes: 9 additions & 9 deletions packages/babel-runtime-corejs3/package.json
Expand Up @@ -17,6 +17,15 @@
"regenerator-runtime": "^0.13.4"
},
"exports": {
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/jsx": [
{
"node": "./helpers/jsx.js",
Expand Down Expand Up @@ -53,15 +62,6 @@
"./helpers/wrapRegExp.js"
],
"./helpers/esm/wrapRegExp": "./helpers/esm/wrapRegExp.js",
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/AwaitValue": [
{
"node": "./helpers/AwaitValue.js",
Expand Down
18 changes: 9 additions & 9 deletions packages/babel-runtime/package.json
Expand Up @@ -17,6 +17,15 @@
"regenerator-runtime": "^0.13.4"
},
"exports": {
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/jsx": [
{
"node": "./helpers/jsx.js",
Expand Down Expand Up @@ -53,15 +62,6 @@
"./helpers/wrapRegExp.js"
],
"./helpers/esm/wrapRegExp": "./helpers/esm/wrapRegExp.js",
"./helpers/asyncIterator": [
{
"node": "./helpers/asyncIterator.js",
"import": "./helpers/esm/asyncIterator.js",
"default": "./helpers/asyncIterator.js"
},
"./helpers/asyncIterator.js"
],
"./helpers/esm/asyncIterator": "./helpers/esm/asyncIterator.js",
"./helpers/AwaitValue": [
{
"node": "./helpers/AwaitValue.js",
Expand Down

0 comments on commit b9ea196

Please sign in to comment.