Основы function* two() { yield 1; yield 2; }
var gen = two(); console.log( gen.next() ); // { value: 1, done: false } console.log( gen.next() ); // { value: 2, done: false } console.log( gen.next() ); // { value: undefined, done: true }
console.log( gen.next() ); // Error: Generator has already finished
Итератор
function* odd(limit) { for (var i = 0; i < limit; i++) { if (i % 2) yield i; } }
for (var i of odd(10)) { console.log(i); } // 1 3 5 7 9
Бесконечные последовательности
function fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; yield current; } }
var sequence = fibonacci(); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next()); // 5
Передача параметров
function* fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; var reset = yield current; if (reset){ fn1 = 1; fn2 = 1; } } }
var sequence = fibonacci(); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next(true)); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2
Рекурсия
function* factorial(n) { return n === 0 ? 1 : n*(yield* factorial(n-1)); }
var gen = factorial(5); console.log(gen.next()); // { value: 120, done: true }
CALLBACK HELL
fs.readdir(source, function(err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function(filename, fileIndex) { gm(source + filename).size(function(err, values) { if (!err) { widths.forEach(function(width, widthIndex) { // ... }.bind(this)) } }) }) } }
Чтение JSON
function readJSONSync(filename) { return JSON.parse(fs.readFileSync(filename, 'utf8')) }
Асинхронный вариант
function readJSON(filename, callback) { fs.readFile(filename, 'utf8', function (err, res){ if (err) return callback(err) callback(null, JSON.parse(res)) }) }
Асинхронный вариант 2
function readJSON(filename, callback) { fs.readFile(filename, 'utf8', function (err, res){ if (err) return callback(err) try { callback(null, JSON.parse(res)) } catch(ex) { callback(ex); } }) }
Асинхронный вариант 3
function readJSON(filename, callback) { fs.readFile(filename, 'utf8', function (err, res){ if (err) return callback(err) try { res = JSON.parse(res) } catch (ex) { return callback(ex) } callback(null, res) }) }
Promise
var Q = require('q');
function readFile(filename) { var deffered = Q.defer(); fs.readFile(filename, function(err, res) { err ? deffered.reject(err) : deffered.resolve(res); }); return deffered.promise; }
Асинхронный вариант с promise
function readJSON(filename){ return readFile(filename).then(function (res){ return JSON.parse(res) }); }
function readJSON(filename){ return readFile(filename).then(JSON.parse}); }
Что не так с promise?
readJSON(file1).then(function(content1) { // do something return readJSON(file2); }).then(function(content2) { // ... }).then(function() { // ... }).then(function() { // ... });
Последовательность операций
db.connect(options).then(function() { return db.getPostById(postId); }).then(function(post) { var tags = post.tags.split(',');
return Q.all(tags.map(function(tag) { return db.getPostsByTag(tag); })).then(function(taggedPosts) { db.disconnect(); }); });
Чтение JSON синхронно
function readJSONSync(filename) { return JSON.parse(fs.readFileSync(filename, 'utf8')) }
Чтение JSON асинхронно
var readJSON = Y(function* (filename) { return JSON.parse(yield readFile(filename, 'utf8')); });
Последовательность операций
Y(function* () { yield db.connect(options);
var post = yield db.getPostById(postId);
var tags = post.tags.split(',');
var taggedPosts = tags.map(function(tag) { return yield db.getPostsByTag(tag); });
db.disconnect(); })();
Обработка ошибок
Y(function* () {
try { var content1 = yield read(file1); var content2 = yield read(file2); var content3 = yield read(file3); } catch(e) { console.log("Error: " + e.message); }
})();
Как это работает?
function* read() { var content = yield readFile('basic.js'); console.log(content.length); }
var gen = read();
var promise = gen.next().value;
promise.then(gen.next.bind(gen), gen.throw.bind(gen));
function Y(fn) { return function() { var generator = fn.apply(this, arguments);
function handle(result) { return result.done ? result.value : result.value.then(function (res){ return handle(generator.next(res)) }, function (err) { return handle(generator.throw(err)) }); }
return hande(generator.next()); } }
SUSPEND
var suspend = require('suspend');
suspend(function* (resume) { return JSON.parse(yield fs.readFile(__filename, 'utf8', resume)); });
CO
function read(file) { return function(fn){ fs.readFile(file, 'utf8', fn); } }
var co = require('co');
co(function *(){ var a = yield read('.gitignore'); var b = yield read('Makefile'); var c = yield read('package.json'); console.log(a.length); console.log(b.length); console.log(c.length); })();
Q.ASYNC
var generator = Q.async(function* () { var ten = yield 10; console.log(ten, 10); var twenty = yield ten + 10; console.log(twenty, 20); var thirty = yield twenty + 10; console.log(thirty, 30); return thirty + 10; });
generator().then(function (forty) { console.log(forty, 40); }, function (reason) { console.log("reason", reason); });
YIELDING
var Y = require('yielding');
var c = Y(function* () { var a = yield 1; var b = yield 2; return a + b; });
console.log( c.once() ); // 1 console.log( c() ); // 3
toArray()
var odd = Y(function* (limit) { for (var i = 0; i < limit; i++) { if (i % 2) yield i; } });
console.log( odd.toArray(10) ); // 1,3,5,7,9
Последовательное выполнение
var get = Y.nwrap( require('request').get );
Y(function* () { var pages = ['http://google.com', 'http://yahoo.com', 'http://bing.com']; var content = pages.map(function(url) { return yield get(url); }); console.log(content.map(function(c) { return c.body.length; })); })();
Параллельное выполнение
var get = Y.nwrap( require('request').get );
Y(function* () { var pages = ['http://google.com', 'http://yahoo.com', 'http://bing.com']; var content = yield pages.map(function(url) { return get(url); }); console.log(content.map(function(c) { return c.body.length; })); })();
SUSPEND-style
Y(function *async() { var result = yield void setTimeout(function () { async.resume(null, 123); }, 200); expect(result).to.be.equal(123); done(); })();
Y(function *async() { var content = yield fs.readFile('test/example.txt', 'utf8', async.resume); })();
var resume = arguments.callee.resume;
Производительность
co(function *(){ var d = +new Date(); for (var i = 1; i < 50000; i++) { var a = yield read('benchmark/1.txt', 'utf8'); } console.log('Reading 50000 times: ' + (+new Date() - d) + 'ms'); })();
CO: ~4.6syielding: ~4.75ssuspend: ~4.8sQ.async: ~6.9s
EXPRESS
app.post('/users', Q.async(function* (req, res) { var user = new User(req.params);
if (yield user.save()) { res.send( JSON.stringify(user) ); } else { res.send(422); } }));
MOCHA
$ mocha --harmony test.js
describe('Some stuff', function() { // ...
it('should do async operation', Y(function* (done) { var a = yield b(); done(); })); });
Асинхронный код
function(args, function(err, res) { function(args2, function(err, res) { function(args3, function(err, res) { // ... }); }); });
Promise
func().then(function(res) { // ... }).then(function(res) { // ... }).then(...);
Генераторы + promise
async(function* () { var res = yield func(); // ... })();
Top Related