Моя любимая особенность генераторов

в 15:58, , рубрики: javascript, node.js, асинхронное программирование, Веб-разработка, лапша

Уже ни раз на хабре писали о том, как работают новые генераторы и я даже не буду первым, кто напишет о их возможности приостанавливать своё выполнение инструкцией yield.

После прочтения статьи «Генераторы в node.js (новый способ борьбы с лапшой)» меня не покидала мысль о том, что можно обойтись без лапши-callBack'ов, но я никак не мог смириться с тем, что для каждой асинхронной функции, которую мы хотим вызвать, нужно писать функцию-обёртку.

И тут родилась идея! Очень простая, а суть её в том, что внутри генератора имеется ссылка на функцию n, которую мы передаём асинхронным функциям как callBack, приостанавливаем генератор при помощи yield, а затем наша функция n продолжает выполнение генератора, передавая в него массив, сформированный из аргументов, с которыми она была вызвана.

Заинтересованных прошу под кат.

Как это выглядит на практике?

А вот так: есть функция Sync, которая, собственно, и делает возможным подобные шаманства. Она создаёт функцию n, затем инициализирует генератор, передавая эту функцию в него и запускает всё это дело.

Sync=function(fn) {
    var gen;
    var callBack=function() {
        gen.next(Array.prototype.slice.call(arguments,0));
    };
    gen=fn(callBack);
    gen.next();
};

А теперь самое интересное!

var fs=require('fs'); //Подключаем модуль FS
console.log(1); //Выводим на экран 1
Sync(function*(cb) { //Передаём в функцию Sync функцию-генератор с одним аргументом, принимающим callBack для асинхронных функций
    console.log(2); //Выводим на экран 2
    yield fs.readFile('sync.js','utf-8',cb); //Вызываем асинхронную функцию и передаём ей ранее принятый callBack, после чего прерываем работу функции
    console.log(4); //Выводим на экран 4
});
console.log(3); //Выводим на экран 3

На выводе мы увидим:
1
2
3
4

4 выводится после 3 потому, что при помощи конструкции yield мы прервали работу функции-генератора,
а затем асинхронная функция readFile завершила выполнение и вызвала callBack, который мы ей передали,
который в свою очередь и продолжил выполнения функции-генератора.

Так можно проследить, что после первой приостановки наш генератор сразу становится отдельным потоком, о чём очень важно помнить. Таким способом не получиться приостановить текущий поток только потому, что вам захотелось использовать асинхронные функции синхронно. Это лишь метод борьбы с лапшой, ничего больше.

А теперь самое вкусное: возврат результатов!

var fs=require('fs');
Sync(function*(cb) {
    var result=yield fs.readFile('sync.js','utf-8',cb);
    console.log(result[1]);
});

Все помнят, что readFile передаёт в callBack 2 аргумента:

  1. Сообщение об ошибке, если она случилась
  2. Данные из файла

Именно по этому мы обратились ко второму элементу массива.

Просто и элегантно, на мой взгляд. Осталось дождаться, когда генераторы выйдут в стабильных сборках nodejs и браузерах.

Автор:

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js