Разница между использованием ключевых слов 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 52

6 ответов:

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

async -await можно использовать yield. В (1) абонент (т. е. async -await драйвер-похож на функцию, которую вы опубликовали) обернет значение в обещание, используя аналогичный алгоритм для 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 ключевое слово?

The await ключевое слово должно использоваться только в async function s, в то время как yield ключевое слово должно использоваться только в генераторе 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 и генераторами.