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

от автора

Уже ни раз на хабре писали о том, как работают новые генераторы и я даже не буду первым, кто напишет о их возможности приостанавливать своё выполнение инструкцией 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 и браузерах.

ссылка на оригинал статьи http://habrahabr.ru/post/228363/


Комментарии

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

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