Уже ни раз на хабре писали о том, как работают новые генераторы и я даже не буду первым, кто напишет о их возможности приостанавливать своё выполнение инструкцией 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 аргумента:
- Сообщение об ошибке, если она случилась
- Данные из файла
Именно по этому мы обратились ко второму элементу массива.
Просто и элегантно, на мой взгляд. Осталось дождаться, когда генераторы выйдут в стабильных сборках nodejs и браузерах.
Автор: