...

среда, 2 июля 2014 г.

[Из песочницы] Моя любимая особенность генераторов

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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий