将函数 * 提升为异步函数 *?
Raising function * into async function *?
假设我有一个函数需要一个生成器和 returns 另一个第一个 n
元素的生成器:
const take = function * (n, xs) {
console.assert(n >= 0);
let i = 0;
for (const x of xs) {
if (i == n) {
break;
}
yield x;
i++;
}
};
这样的用法:
const evens = function * () {
let i = 0;
while (true) {
yield i;
i += 2;
}
};
for (const x of take(10, evens())) {
console.log(x);
}
现在假设 evens
也是 async
(设置见 this answer):
const evensAsync = async function * () {
let i = 0;
while (true) {
yield i;
i += 2;
}
};
这当然不适用于 take
:
const main = async () => {
for await (const x of take(10, evensAsync())) {
console.log(x);
}
};
main().catch(e => console.error(e));
现在,我可以定义一个 take
变体,即 async
:
const takeAsync = async function * (n, xs) {
console.assert(n >= 0);
let i = 0;
for await (const x of xs) {
if (i == n) {
break;
}
yield x;
i++;
}
};
const main = async () => {
for await (const x of takeAsync(10, evensAsync())) {
console.log(x);
}
};
main().catch(e => console.error(e));
... 但是真麻烦!
有没有办法在 JavaScript 中自动 "asyncify" 生成器函数?
Is there a way to automatically "asyncify" generator functions in JavaScript?
没有。异步和同步发电机差别太大了。您将需要 take
函数的两种不同实现,没有办法解决它。
但是您可以动态地select选择哪一个:
async function* takeAsync(asyncIterable) { … }
function* takeSync(iterable) { … }
function take(obj) {
if (typeof obj[Symbol.asyncIterator] == "function") return takeAsync(obj);
if (typeof obj[Symbol.iterator] == "function") return takeSync(obj);
throw new TypeError("not an iterable object");
}
通常不可能编写一个可以像 asyncify(takeSync)(10, evensAsync())
一样使用的函数 asyncify
。人们可能能够将适用于 takeSync
和 mapSync
的东西组合在一起,这取决于它们每个输入项只输出一个项这一事实,但它会非常脆弱并且不适用于其他迭代函数像 filter
.
也就是说,当然可以概括和提供抽象。
function taker(n) {
return {
done: n > 0,
*step(element) { /* if (n > 0) */ yield element; return taker(n-1); },
result: null
}
}
function mapper(fn) {
return {
done: false,
*step(element) { yield fn(element); return this; }
result: null
}
}
function makeIterator(t) {
return function*(iterable) {
for (let element of iterable) {
t = yield* t.step(element);
if (t.done) break;
}
return t.result;
};
}
function makeAsyncIterator(t) {
return async function*(asyncIterable) {
for await (let element of asyncIterable) {
t = yield* t.step(element);
if (t.done) break;
}
return t.result;
};
}
(无耻地撕掉 transducer concept - 也没有测试任何东西)
假设我有一个函数需要一个生成器和 returns 另一个第一个 n
元素的生成器:
const take = function * (n, xs) {
console.assert(n >= 0);
let i = 0;
for (const x of xs) {
if (i == n) {
break;
}
yield x;
i++;
}
};
这样的用法:
const evens = function * () {
let i = 0;
while (true) {
yield i;
i += 2;
}
};
for (const x of take(10, evens())) {
console.log(x);
}
现在假设 evens
也是 async
(设置见 this answer):
const evensAsync = async function * () {
let i = 0;
while (true) {
yield i;
i += 2;
}
};
这当然不适用于 take
:
const main = async () => {
for await (const x of take(10, evensAsync())) {
console.log(x);
}
};
main().catch(e => console.error(e));
现在,我可以定义一个 take
变体,即 async
:
const takeAsync = async function * (n, xs) {
console.assert(n >= 0);
let i = 0;
for await (const x of xs) {
if (i == n) {
break;
}
yield x;
i++;
}
};
const main = async () => {
for await (const x of takeAsync(10, evensAsync())) {
console.log(x);
}
};
main().catch(e => console.error(e));
... 但是真麻烦!
有没有办法在 JavaScript 中自动 "asyncify" 生成器函数?
Is there a way to automatically "asyncify" generator functions in JavaScript?
没有。异步和同步发电机差别太大了。您将需要 take
函数的两种不同实现,没有办法解决它。
但是您可以动态地select选择哪一个:
async function* takeAsync(asyncIterable) { … }
function* takeSync(iterable) { … }
function take(obj) {
if (typeof obj[Symbol.asyncIterator] == "function") return takeAsync(obj);
if (typeof obj[Symbol.iterator] == "function") return takeSync(obj);
throw new TypeError("not an iterable object");
}
通常不可能编写一个可以像 asyncify(takeSync)(10, evensAsync())
一样使用的函数 asyncify
。人们可能能够将适用于 takeSync
和 mapSync
的东西组合在一起,这取决于它们每个输入项只输出一个项这一事实,但它会非常脆弱并且不适用于其他迭代函数像 filter
.
也就是说,当然可以概括和提供抽象。
function taker(n) {
return {
done: n > 0,
*step(element) { /* if (n > 0) */ yield element; return taker(n-1); },
result: null
}
}
function mapper(fn) {
return {
done: false,
*step(element) { yield fn(element); return this; }
result: null
}
}
function makeIterator(t) {
return function*(iterable) {
for (let element of iterable) {
t = yield* t.step(element);
if (t.done) break;
}
return t.result;
};
}
function makeAsyncIterator(t) {
return async function*(asyncIterable) {
for await (let element of asyncIterable) {
t = yield* t.step(element);
if (t.done) break;
}
return t.result;
};
}
(无耻地撕掉 transducer concept - 也没有测试任何东西)