Пример сопрограммы - PullRequest
       30

Пример сопрограммы

0 голосов
/ 27 апреля 2018

Изучая сопрограммы в JavaScript, я не могу обернуться вокруг следующего примера :

coroutine(function* () {
  let posts = yield fetchPosts();
  let promises = posts.map(post => fetchComments(post));
  let comments = yield Promise.all(promises);
  displayComments(comments);
})

function coroutine(fn) {
  let gen = fn();
  let doNext = (data) => {
    let next = gen.next(data);
    if (!next.done) {
      return next.value.then(doNext);
    }
  };
  doNext();
}

Мне кажется, что при вызове coroutine в приведенном выше примере, doNext будет кумулятивно вызываться дважды.

Когда вызывается coroutine, функция генератора останавливает выполнение в первой строке let posts = yield fetchposts();; управление возвращается к функции coroutine. В строке let next = gen.next(data); управление передается обратно в функцию генератора.

Теперь, насколько я понимаю, fetchposts в данный момент вернет Обещание, которое включает все сообщений. Следовательно, это будет первый запуск doNext.

Второй запуск doNext начнется, когда функция генератора остановит выполнение в строке let comments = yield Promise.all(promises);; возвращение управления к функции coroutine. В этот момент next.value.then(doNext) больше не имеет смысла для меня. Я ожидаю, что next.value будет обещанием, которое сможет разрешить комментарии.

А именно, Promises.all(promises) передается обратно функцией генератора. Следовательно, это должно разрешиться, как только все комментарии будут извлечены post Promises.

Очевидно, я не вижу этого правильно, так как объяснение, которое я дал, не соответствует фактическому выполнению вызова. Что я вижу не так?

1 Ответ

0 голосов
/ 30 апреля 2018

Может быть, это поможет расширить рекурсивную функцию doNext для визуализации потока выполнения:

function* fn() {
  let posts = yield fetchPosts();
  let promises = posts.map(post => fetchComments(post));
  let comments = yield Promise.all(promises);
  displayComments(comments);
}

let gen = fn();
let data;
let next = gen.next(data); // data is undefined
                           // generator runs until first yield
                           // next.value becomes fetchPosts()
// assert(!next.done)
next.value.then(data => {
  let next = gen.next(data); // data is result of fetchPosts()
                             // generator runs until second yield:
                             //  posts becomes passed value (data)
                             //  promises becomes posts.map(…)
                             // next.value becomes Promise.all(…)
  // assert(!next.done)
  next.value.then(data => {
    let next = gen.next(data); // data is result of Promise.all(…)
                               // generator runs until the end:
                               //  comments becomes passed value (data)
                               //  comments are logged
                               // next.value becomes undefined
    // assert(next.done)
  });
});
...