Подтвердить что ты не робот

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

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

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 с генераторами в этой статье, где он описывает функцию "порождения" Асинхронные функции ES7.

4b9b3361

Ответ 1

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

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

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

    const foo = await 3;
    const bar = await new Promise(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 a generator

    const foo = yield 3;    //  <<< now it yield, not await
    const bar = yield new Promise(resolve => resolve(7));
    const baz = bar * foo;
    console.log(baz);

}));

ты делаешь математику.

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

Вы можете увидеть больше объяснений для этого здесь:https://www.promisejs.org/generators/

Ответ 2

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, чтобы развернуть полученное значение в качестве обещания и передать свое разрешенное значение обратно, повторяя, пока функция не вернет свое окончательное значение.

Ответ 3

Какая разница между ключевым словом await и ключевым словом yield?

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

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

Да, await выведет Promise.resolve на ожидаемое значение.

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

Ответ 4

ТЛ; др

Используйте async/await 99% времени на генераторах. Почему?

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

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

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

См. очень подробное объяснение Async/Await vs. Generators.

Ответ 5

Попробуйте эти тестовые программы, которые я использовал для понимания 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');

Ответ 6

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

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