Разница между выходом async/await и ES6 с генераторами

Я как раз читал эту фантастическую статью -

https://www.promisejs.org/generators/

и он четко выделяет эту функцию, которая является вспомогательной функцией для обработки функций генератора:

function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);

      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

что я предполагаю, более или менее так, как ключевое слово async реализовано с async/await. Итак, вопрос в том, если это так, то в чем, черт возьми, разница между await ключевое слово и yield ключевое слово? Делает await всегда превращайте что-то в обещание, тогда как yield не дает такой гарантии? Это мое лучшее предположение!

вы также можете увидеть, как async / await похож на yield с генераторами в этой статье, где он описывает функцию "spawn":https://jakearchibald.com/2014/es7-async-functions/

6 ответов


yield можно считать строительным блоком await. yield принимает заданное значение и передает его вызывающему. Затем абонент может делать что угодно с этим значением (1). Позже вызывающий абонент может вернуть значение генератору (через generator.next()), который становится результатом yield выражение (2) или ошибка, которая будет отображаться как вызванная yield выражение (3).

async-await можно использовать yield. В (1) абонент (то есть async-await driver-аналогично функции, которую вы опубликовали) обернет значение в обещание, используя аналогичный алгоритм для new Promise(r => r(value) (обратите внимание, не Promise.resolve, но это не большая проблема). Затем он ждет, когда обещание разрешится. Если он выполняет, он передает выполненное значение обратно в (2). Если он отклоняет, он выбрасывает причину отклонения как ошибку в (3).

Итак, полезность async-await это оборудование, которое использует yield развернуть дали значение как обещание и передать его разрешенное значение обратно, повторяя до тех пор, пока функция не вернет его конечное значение.


ну, оказывается, что существует очень тесная связь между async / await и генераторами. И я считаю, что async / await всегда будет построен на генераторах. Если вы посмотрите, как Babel транспилирует async / await:

Вавилон берет это:

this.it('is a test', async function () {

    const foo = await 3;
    const bar = await new Promise(function (resolve) {
        resolve('7');
    });
    const baz = bar * foo;
    console.log(baz);

});

и превращает его в этот

function _asyncToGenerator(fn) {
    return function () {
        var gen = fn.apply(this, arguments);
        return new Promise(function (resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch (error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    return Promise.resolve(value).then(function (value) {
                        return step("next", value);
                    }, function (err) {
                        return step("throw", err);
                    });
                }
            }

            return step("next");
        });
    };
}


this.it('is a test', _asyncToGenerator(function* () {   // << now it's a generator

    const foo = yield 3;    // << now it's yield not await
    const bar = yield new Promise(function (resolve) {
        resolve('7');
    });
    const baz = bar * foo;
    console.log(baz);
}));

вы делаете математику.

это делает его похожим на ключевое слово async-это просто функция обертки, но если это так, то await просто превращается в урожай, наверное, будет немного позже, когда они станут родными.


какого черта разница между await ключевое слово и yield сайта?

на await ключевое слово должно использоваться только в async functions, в то время как yield ключевое слово должно использоваться только в generator function*s. И они, очевидно, тоже разные - один возвращает обещания, другой возвращает генераторы.

тут await всегда превращайте что-то в обещание, тогда как yield не дает таких гарантия?

да await будем называть Promise.resolve о ожидаемом значении.

yield просто дает значение вне генератора.


попробуйте эти тестовые программы, которые я использовал для понимания await / async с обещаниями

программа #1: без обещаний она не запускается последовательно

function functionA() {
    console.log('functionA called');
    setTimeout(function() {
        console.log('functionA timeout called');
        return 10;
    }, 15000);

}

function functionB(valueA) {
    console.log('functionB called');
    setTimeout(function() {
        console.log('functionB timeout called = ' + valueA);
        return 20 + valueA;
    }, 10000);
}

function functionC(valueA, valueB) {

    console.log('functionC called');
    setTimeout(function() {
        console.log('functionC timeout called = ' + valueA);
        return valueA + valueB;
    }, 10000);

}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

программа 2 : с обещаниями :

function functionA() {
    return new Promise((resolve, reject) => {
        console.log('functionA called');
        setTimeout(function() {
            console.log('functionA timeout called');
            // return 10;
            return resolve(10);
        }, 15000);
    });   
}

function functionB(valueA) {
    return new Promise((resolve, reject) => {
        console.log('functionB called');
        setTimeout(function() {
            console.log('functionB timeout called = ' + valueA);
            return resolve(20 + valueA);
        }, 10000);

    });
}

function functionC(valueA, valueB) {
    return new Promise((resolve, reject) => {
        console.log('functionC called');
        setTimeout(function() {
            console.log('functionC timeout called = ' + valueA);
            return resolve(valueA + valueB);
        }, 10000);

    });
}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

tldr;

используйте Async / Await 99% времени над генераторами. Почему?

  1. Async / Await напрямую заменяет наиболее распространенный рабочий поток цепочек обещаний, позволяющий объявлять код как синхронный, что значительно упрощает его.

  2. генераторы абстрактный вариант использования, когда вы вызываете серию асинхронных операций, которые зависят друг от друга и в конечном итоге будут в состоянии "готово". Самый простой примером может быть пролистывание результатов, которые в конечном итоге возвращают последний набор, но вы будете вызывать страницу только по мере необходимости, а не сразу подряд.

  3. Async / Await на самом деле является абстракцией, построенной поверх генераторов, чтобы упростить работу с обещаниями.

см. очень подробное объяснение Async / Await против генераторов


во многих отношениях генераторы являются надмножеством async / await. Прямо сейчас async / await имеет более чистые трассировки стека, чем co, самый популярный асинхронный / ожидающий генератор на основе lib. Вы можете реализовать свой собственный аромат async / await с помощью генераторов и добавить новые функции, такие как встроенная поддержка yield о не-обещаниях или построении его на наблюдаемых RxJS.

короче говоря, генераторы дают вам больше гибкости, а генераторные библиотеки обычно имеют больше функций. Но async / await является основной частью языка, он стандартизирован и не изменится под вами, и вам не нужна библиотека для его использования. У меня есть блоге С более подробной информацией о разнице между async / await и генераторами.