Ответ 1
Есть ли способ автоматически "асинхронировать" функции генератора в JavaScript?
Нет. Асинхронные и синхронные генераторы слишком разные. Вам понадобятся две различные реализации вашей функции take
, там нет возможности.
Однако вы можете динамически выбирать, какой из них выбрать:
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
, которая может использоваться как asyncify(takeSync)(10, evensAsync())
. Можно было бы взломать что-то вместе, что работает для 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;
};
}
(который бесстыдно срывает концепцию преобразователя - также не тестировался ни на что)