WuT*_*hat 5 javascript generator async-await ecmascript-6 ecmascript-next
我有2个非常相关的问题.一个实际的,与我所遇到的真正问题有关,还有一个理论问题.让我从后者开始:
一般来说,在回调或承诺中产生收益并没有多大意义.问题(以及其他问题)是在多个产量之间可能存在竞争,无法判断发电机何时结束等.
function* this_is_nonsense() {
setTimeout(() => { yield 'a'; }, 500);
setTimeout(() => { yield 'b'; }, 500);
return 'c';
}
Run Code Online (Sandbox Code Playgroud)
当使用async/await时,这些问题似乎实际上已经消失了
function timeout(ns) {
return new Promise((resolve) => setTimeout(resolve, ns));
}
async function* this_could_maybe_make_sense() {
// now stuff is sequenced instead of racing
await timeout(500);
yield 'a';
await timeout(500);
yield 'b';
return 'c';
}
Run Code Online (Sandbox Code Playgroud)
我假设目前没有这样的事情(如果错误请纠正我!).所以我的第一个问题是,使用async/await生成器是否存在概念性问题?如果没有,是否在任何路线图上?似乎实现需要的不仅仅是将两个功能混合在一起.
这是我所拥有的真实代码问题的人为版本.
想象一下编写一个遍历维基百科的函数,从一些任意页面开始并反复跟随链接(无论如何 - 可以是深度优先,广度优先还是其他东西).假设我们可以异步爬行维基百科页面以获取链接页面.就像是:
async function traverseWikipedia(startLink) {
const visited = {};
const result = [];
function async visit(link) {
if (visited[link]) { return; }
visited[link] = true;
result.push(link);
const neighboringLinks = await getNeighboringWikipediaLinks(link);
for (const i = 0; i < neighboringLinks.length; i++) {
await visit(neighboringLinks[i]);
}
// or to parallelize:
/*
await Promise.all(
neighboringLinks.map(
async (neighbor) => await visit(neighbor)
)
);
*/
);
await visit(startLink);
return result;
}
Run Code Online (Sandbox Code Playgroud)
这很好......除了有时候我只想要前10个链接,这会抓住大量的链接.有时我正在搜索包含子串'Francis'的前10个.我想要一个懒惰的版本.
从概念上讲,Promise<Array<T>>我希望Generator<T>或至少不是返回类型Generator<Promise<T>>.我希望消费者能够做任意逻辑,包括异步停止条件.这是一个假的,完全错误的(我假设)版本:
function* async traverseWikipedia(startLink) {
const visited = {};
function* async visit(link) {
if (visited[link]) { return; }
visited[link] = true;
yield link;
const neighboringLinks = await getNeighboringWikipediaLinks(link);
for (const i = 0; i < neighboringLinks.length; i++) {
yield* await visit(neighboringLinks[i]);
}
);
yield* await visit(startLink);
}
Run Code Online (Sandbox Code Playgroud)
这与Q1的废话相同,将异步/等待+生成器组合起来就像它做了我想要的那样.但如果我确实拥有它,我可以像这样消费:
function find10LinksWithFrancis() {
const results = []
for (const link of traverseWikipedia()) {
if (link.indexOf('Francis') > -1) {
results.push(link);
}
if (results.length === 10) {
break;
}
}
return results;
}
Run Code Online (Sandbox Code Playgroud)
但我也可以搜索不同数量的结果和字符串,具有异步停止条件等.例如,它可以显示10个结果,当用户按下按钮时,继续爬行并显示下一个10.
只要我仍然为消费者提供相同的API,我只使用promise/callbacks,没有async/await就可以了.
生成器的调用者可以执行异步操作.所以我认为有可能实现这一点,其中生成器函数产生邻居的承诺,并且由调用者调用gen.next生成的实际邻居.就像是:
function* traverseWikipedia(startLink) {
const visited = {};
function* visit(link) {
if (visited[link]) { return; }
visited[link] = true;
yield { value: link };
const neighboringLinks = yield { fetch: getNeighboringWikipediaLinks(link) };
for (const i = 0; i < neighboringLinks.length; i++) {
yield* visit(neighboringLinks[i]);
}
);
yield* visit(startLink);
}
Run Code Online (Sandbox Code Playgroud)
但是使用起来并不是很好......你必须检测实际结果的情况而不是生成器要求你给它回答答案,它的类型安全性较低等等.我只是想要一些看起来像Generator<Promise<T>>和相对简单的东西使用它的方式.这可能吗?
编辑添加:发生在我身上你可以实现这个,如果生成器函数的主体可以访问它以某种方式返回的生成器...不知道如何做到这一点虽然
再次编辑添加消费者的例子,并注意:如果消费者也是生成器,我也没关系,如果这有帮助的话.我认为下面有某种引擎层可能是可能的,让人想起redux-saga.不确定是否有人写过这样的东西
| 归档时间: |
|
| 查看次数: |
694 次 |
| 最近记录: |