Реализация Async и Await с генераторами

realizacziya async i await s generatorami

Сейчас мы можем писать наш асинхронный код синхронно благодаря асинхронный и ждать ключевые слова. Это облегчает чтение и понимание. Однако недавно я подумал, как можно достичь такого же эффекта без этих ключевых слов.

Это оказывается достаточно просто, поскольку поведение о асинхронный и ждать можно легко эмулировать с помощью генераторов. Давай посмотрим!

Давайте, клонируйте хранилище и начнём.

Генераторы

Я предполагаю, что у вас почти нет опыта работы с генераторами, поскольку, честно говоря, большинство времени они не особенно полезны и вы можете легко обойтись без них. Так что не волнуйтесь — мы начнем с короткого напоминания.

Генераторы — это объекты, созданные функциями генератора — функциями an * (звездочка) у их имени.

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

Рассмотрим этот пример:

const generator = (function*() {
  // waiting for .next()
  const a = yield 5;
  // waiting for .next()
  console.log(a); // => 15
})();

console.log(generator.next()); // => { done: false, value: 5 }
console.log(generator.next(15)); // => { done: true, value: undefined }

Учитывая, что это абсолютные основы, я бы рекомендовал, прежде чем прокручивать дальше, прочитать эту статью, чтобы понять, что здесь происходит.

Если вы чувствуете, что понимаете основные идеи, мы можем двигаться дальше.

Подожди минутку

Вы никогда не задумывались, как ждать действительно работает?

Каким-то образом он просто ждет, пока наше обещание вернет значение и продолжит исполнение. Для меня это кажется тем, что генератор мог бы сделать после небольшой настройки.

Что мы можем сделать, так это просто взять каждое полученное значение, поместить его в обещание, а затем ждать, пока обещание будет решено. После этого мы просто возвращаем его в генератор, вызывая generator.next(resolvedValue).

Похоже на план. Но сначала давайте напишем несколько тестов, чтобы убедиться, что все работает, как ожидалось.

Какие наши asynq функция должна производить:

  • подождите асинхронного кода, прежде чем продолжить выполнение
  • вернуть а обещание со значением, возвращаемым из функции
  • сделать попробовать/поймать работа над асинхронным кодом

Примечание: поскольку мы используем генераторы, наши ждать становится урожайность.

import { asynq } from '../src';

describe('asynq core', () => {
  test('Waits for values (like await does)', () => {
    return asynq(function*() {
      const a = yield Promise.resolve('a');
      expect(a).toBe('a');
    });
  });

  test('Catches the errors', () => {
    return asynq(function*() {
      const err = new Error('Hello there');

      try {
        const a = yield Promise.resolve('a');
        expect(a).toBe('a');

        const b = yield Promise.resolve('b');
        expect(b).toBe('b');

        const c = yield Promise.reject(err);
      } catch (error) {
        expect(error).toBe(err);
      }

      const a = yield Promise.resolve(123);
      expect(a).toBe(123);
    });
  });

  test('Ends the function if the error is not captured', () => {
    const err = new Error('General Kenobi!');

    return asynq(function*() {
      const a = yield Promise.reject(err);
      const b = yield Promise.resolve('b');
    }).catch((error) => {
      expect(error).toBe(err);
    });
  });

  test('Returns a promise with the returned value', () => {
    return asynq(function*() {
      const value = yield Promise.resolve(5);
      expect(value).toBe(5);

      return value;
    }).then((value) => {
      expect(value).toBe(5);
    });
  });
});

Хорошо, здорово! Теперь можно говорить о реализации.

Наши asynq функция принимает в качестве параметра генератор функции – вызывая ее, мы создаем генератор.

Чтобы убедиться, мы звоним isGeneratorLike который проверяет, является ли полученное значение объектом и имеет ли методы следующий и бросать.

Затем, рекурсивно, мы потребляем каждый урожайность ключевое слово по звонку generator.next(ensuredValue). Ждем, пока возвращенное обещание будет рассчитано, а затем возвращаем его результат обратно к генератору, повторяя весь процесс.

Мы также должны прикрепить выловить обработчик, так что если функция генерирует исключение, мы можем поймать его и вернуть исключение внутри функции, вызвав generator.throw(ошибка).

Теперь все возможные ошибки будут обработаны выловить. Если бы не было а попробовать/поймать блок на месте, ошибка просто остановит исполнение вообще – как и любое необработанное исключение – и наша функция вернет отклоненное обещание.

Когда генератор будет завершен, мы возвращаем значение генератора в обещании.

import { isGeneratorLike } from './utils';

type GeneratorFactory = () => IterableIterator<any>;

function asynq(generatorFactory: GeneratorFactory): Promise<any> {
  const generator = generatorFactory();

  if (!isGeneratorLike(generator)) {
    return Promise.reject(
      new Error('Provided function must return a generator.'),
    );
  }

  return (function resolve(result) {
    if (result.done) {
      return Promise.resolve(result.value);
    }

    return Promise.resolve(result.value)
      .then((ensuredValue) => resolve(generator.next(ensuredValue)))
      .catch((error) => resolve(generator.throw(error)));
  })(generator.next());
}

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

Подведению

Хотя эта реализация, вероятно, не используется в механизмах JavaScript, это, несомненно, приятно иметь возможность совершить что-то подобное самостоятельно.

Не стесняйтесь просматривать код снова. Чем лучше вы понимаете основные идеи, тем больше вы сможете оценить блеск творцов асинхронный и ждать ключевые слова.

Спасибо, что читаете! Надеюсь, вы нашли эту статью информативной. Я также надеюсь, что это помогло вам увидеть, что в этом нет магии. асинхронный и ждать ключевые слова, и которые можно легко заменить генераторами.

Если у вас есть вопросы или комментарии, не стесняйтесь оставлять их в разделе комментариев ниже или отправлять мне сообщение.

Просмотрите мои социальные сети!

Присоединяйтесь к моей рассылке!

Первоначально опубликовано на www.mcieslar.com 6 августа 2018 года.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *