询问不能直接转换为手动实现的异步迭代的异步生成器的示例

And*_*sta 10 javascript iteration asynchronous generator es2018

异步生成器使用内部队列来处理nextthrowreturn方法的同步调用。

我试图构造一种情况,其中此队列对于成功进行迭代本身是必需的。因此,我正在寻找某些情况,而没有自定义重新实现队列的手动实现异步迭代接口是不够的。

以下是一个示例,但是效果不是很好,因为不能保持一般的时间一致性,但是迭代结果在每个步骤中都是正确的:

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请求。不要误会我,我不是说我们可以视而不见。关键是异步迭代本身的每个步骤都将永不中断。

我希望看到一种情况,不是太不切实际,如果不将所有后续调用都排队,则迭代的每一步都可能受到损害。

Jon*_*lms 5

以下场景:

您有一个数据集流传入,例如来自某些 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)