And*_*sta 10 javascript iteration asynchronous generator es2018
异步生成器使用内部队列来处理next,throw和return方法的同步调用。
我试图构造一种情况,其中此队列对于成功进行迭代本身是必需的。因此,我正在寻找某些情况,而没有自定义重新实现队列的手动实现异步迭代接口是不够的。
以下是一个示例,但是效果不是很好,因为不能保持一般的时间一致性,但是迭代结果在每个步骤中都是正确的:
function aItsFactory() {
let i = 1;
return {
async next() {
if(i > 5) return Promise.resolve({ value: void 0, done: true });
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${i++}`).then(x => x.json());
return Promise.resolve({ value: res, done: false });
},
[Symbol.asyncIterator]() {
return this;
}
}
}
const ait = aItsFactory();
// general time consistency is lost, because e.g. the fourth call
// is started with the previous three and it could end before the others.
// But the 'i' state is correctly shared so the fifth call
// is correctly requesting the element number five to the source
// and the last call will correctly receive { done: true }
;(async () => {
ait.next();
ait.next();
ait.next();
ait.next();
console.log(await ait.next()); // { done: false, value: { userId: 1, id: 5, title: ... } }
console.log(await ait.next()); // { done: true, value: undefined }
})();
Run Code Online (Sandbox Code Playgroud)
可以说没有适当的队列,迭代概念本身就会丢失。那是因为活动的并行next调用。
无论如何,我想找到一些例子,也包括一些琐碎的例子,这些例子清楚地表明,与手动生成异步迭代接口相比,异步生成器是创建格式良好的异步可迭代对象的更好方法。
------编辑------
让我们谈谈一种改善的情况:
function aItsFactory() {
let i = 1;
let done = false;
return {
async next() {
if (done) return Promise.resolve({
done: true,
value: undefined
});
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${i++}`).then(x => x.json());
if (Object.keys(res).length === 0) { // the jsonplaceholder source is out of bounds
done = true;
return Promise.resolve({
done: true,
value: undefined
});
} else {
return Promise.resolve({
done: false,
value: res
});
};
},
[Symbol.asyncIterator]() {
return this;
}
}
}
const ait = aItsFactory();
// now lot of sync call to 'ait.next'
Run Code Online (Sandbox Code Playgroud)
这里的done解析是完全异步的。从异步迭代的角度来看,该代码是错误的,因为每个next调用都应强制await执行前一个的结果,以了解它是否是最后一个有效的迭代。在这种情况下,电流next什么都不做,立即返回Promise.resolve({done:true, value:undefined})。这只有通过同步next调用队列才有可能。
但是实际上,越界,ait.next()反复调用的主要风险是一些无用的AJAX请求。不要误会我,我不是说我们可以视而不见。关键是异步迭代本身的每个步骤都将永不中断。
我希望看到一种情况,不是太不切实际,如果不将所有后续调用都排队,则迭代的每一步都可能受到损害。
以下场景:
您有一个数据集流传入,例如来自某些 API。您想要对每个数据集进行一些繁重的计算,这就是您将数据集发送给另一个工作人员的原因。但有时 API 可能会一次发送多个数据集,并且您不希望同时运行大量工作线程,而是希望有有限数量的工作线程。在该数据集中,您正在搜索特定结果。使用异步迭代器,您可以将其写为:
const incoming = createSomeAsyncIterator();
async function processData() {
let done, value;
while(!done) {
({ done, value } = await incoming.next());
if(!done) {
const result = await searchInWorker(value);
if(result) {
incoming.return();
return result;
}
}
}
}
// Consume tasks in two workers.
Promise.race([
processData(), processData()
]).then(gold => /*...*/);
Run Code Online (Sandbox Code Playgroud)
.next()如果不能按顺序返回数据集,上面的代码将会失败。尽管搜索已经完成,但其中一名工人可能仍会继续工作。或者两个工作人员可能会处理同一个数据集。
或者速率限制示例(从 Bergi 盗用:)):
async function* rateLimit(limit, time) {
let count = 0;
while(true) {
if(count++ >= limit) {
await delay(time);
count = 0;
}
yield; // run api call
}
}
const userAPIRate = rateLimit(10, 1000);
async function getUser(id) {
await userAPIRate.next();
return doCall("/user/", id);
}
Run Code Online (Sandbox Code Playgroud)
或者想象一下你想以某种形式的图库(在 React 中)显示一系列图片:
const images = streamOfImages();
const Image = () => {
const [image, setImage] = useState(null);
useEffect((async ( ) => {
if(image) await delay(10000); // show image at least 10secs
const { value } = await images.next();
setImage(value);
}, [image]);
return <img src={image || "loading.png"} />;
};
const Gallery = () => <div>
<Image /> <Image /> <Image />
</div>;
Run Code Online (Sandbox Code Playgroud)
另一种是将数据分配给工作人员,以便一次运行一个进程:
const worker = (async function* () {
let task;
while(true) task = yield task && await doInWorker(task);
})();
worker.next();
worker.next("task 1").then(taskOne => ...);
worker.next("task 2").then(taskTwo => ...);
Run Code Online (Sandbox Code Playgroud)