Open RubyLouvre opened 9 years ago
co 要求传入一个生成器函数, 然后让里面的所有异步方法同步执行(从上往下执行),最后返回一个Promise
var co = require("co")
var delay = function(a){
return function(fn){
var now = new Date
setTimeout(function(){
console.log(a + "--------")
fn(null, new Date - now)
}, a)
}
}
co(function* () {
var a = yield delay(1000);
console.log("执行完a")
var b = yield delay(1500);
console.log("执行完b")
return [a,b];
}).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});
co方法只是让参数函数的内部变成一种类似PHP同步环境, 但不方便我们往这个环境传参
因此就有了co.wrap, 这也是koa里面使用的方法
将上面的例子改一下
var co = require("co")
var delay = function(a){
return function(fn){
var now = new Date
setTimeout(function(){
console.log(a + "--------")
fn(null, new Date - now)
}, a)
}
}
var fn = co.wrap(function* (val) {
var a = yield delay(val);
console.log("执行完a")
var b = yield delay(val);
console.log("执行完b")
return [a,b];
});
fn(1000).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});
co方法只是让参数函数的内部变成一种类似PHP同步环境, 但不方便我们往这个环境传参
因此就有了co.wrap, 这也是koa里面使用的方法
将上面的例子改一下
var co = require("co")
var delay = function(a){
return function(fn){
var now = new Date
setTimeout(function(){
console.log(a + "--------")
fn(null, new Date - now)
}, a)
}
}
var fn = co.wrap(function* (val) {
var a = yield delay(val);
console.log("执行完a")
var b = yield delay(val);
console.log("执行完b")
return [a,b];
});
fn(1000).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});
我们再看koa的核心代码
app.callback = function(){
var mw = [respond].concat(this.middleware);
var fn = this.experimental
? compose_es7(mw)
: co.wrap(compose(mw));
var self = this;
if (!this.listeners('error').length) this.on('error', this.onerror);
return function(req, res){
res.statusCode = 404;
var ctx = self.createContext(req, res);
onFinished(res, ctx.onerror);
fn.call(ctx).catch(ctx.onerror);
}
};
mw 为一个装着许多生成器函数的数组 this.experimental是es7用的,因此只会走co.wrap分支
上面的分码可以简化为
app.callback = function(){
var mw = [respond].concat(this.middleware);
var fn = co.wrap(compose(mw));
var self = this;
if (!this.listeners('error').length) this.on('error', this.onerror);
return function(req, res){
res.statusCode = 404;
var ctx = self.createContext(req, res);
onFinished(res, ctx.onerror);
fn.call(ctx).catch(ctx.onerror);
}
};
var co = require("./co")
var compose = require("./compose")
function *m1(next){
var start = new Date;
console.log("start=======1111");
yield next;
console.log("end=======1111");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
function *m2(next){
var start = new Date;
console.log("start=======2222");
yield next;
console.log("end=======2222");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
function *m3(next){
var start = new Date;
console.log("start=======3333");
yield next;
console.log("end=======3333");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
var list = [m1, m2, m3]
var fn = co.wrap(compose(list));
fn.call({method: "get", url: "localhost"}).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});
改得更像些应该是这样
var co = require("./co")
var compose = require("./compose")
function *respond(next) {
console.log("first !")
var last = yield *next;
console.log(last+"!")
}
function *m1(next){
var start = new Date;
console.log("start=======1111");
yield next;
console.log("end=======1111");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
return "last "
}
function *m2(next){
var start = new Date;
console.log("start=======2222");
yield next;
console.log("end=======2222");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
function *m3(next){
var start = new Date;
console.log("start=======3333");
yield next;
console.log("end=======3333");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
var list = [respond, m1, m2, m3]
var fn = co.wrap(compose(list));
fn.call({method: "get", url: "localhost"}).catch(function (err) {
console.error(err.stack);
});
var co = require("./co")
var compose = require("./compose")
function delay(time) {
return function(fn) {//这是一个普通函数,使用thunkToPromise
setTimeout(function() {
fn(null, time)//null表示没有错
}, time)
}
}
co(function* () {
var a = yield delay(200)
a = yield delay(a + 100)
a = yield delay(a+ 150)
return a
}).then(function(time) {
console.log(time)
})
加入 定时器
var co = require("./co")
var compose = require("./compose")
function *respond(next) {
console.log("first !")
var last = yield *next;
console.log(last+"!")
}
function *m1(next){
var start = new Date;
console.log("start=======1111");
yield next;
console.log("end=======1111");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
return "last "
}
function *m2(next){
var start = new Date;
console.log("start=======2222");
yield next;
console.log("end=======2222");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
function delay(time) {
return function(fn) {//这是一个普通函数,使用thunkToPromise
setTimeout(function() {
console.log("setTimeout")
fn(null, time)//null表示没有错
}, time)
}
}
function *m3(next){
var start = new Date;
console.log("start=======3333");
yield delay(300)
console.log("start=======333 setTimeout");
yield next;
console.log("end=======3333");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
var list = [respond, m1, m2, m3]
var fn = co.wrap(compose(list));
fn.call({method: "get", url: "localhost"}).catch(function (err) {
console.error(err.stack);
});
将一个普通的node异步方法改成可以yield的方法 可以使用以下方式
var helper = function (fn) {
return function () {
var args = [].slice.call(arguments);
var pass;
args.push(function () { // 在回调函数中植入收集逻辑
if (pass) {
pass.apply(null, arguments);
}
});
fn.apply(null, args);
return function (fn) { // 传入一个收集函数
pass = fn;
};
};
};
var readFile = function(filename) {
return function(cb){
fs.readFile(filename,cb);
}
};
或者使用thunkify
var co = require("./co")
var compose = require("./compose")
var thunkify = require("thunkify")
var fs = require("fs")
function *respond(next) {
console.log("first !")
var last = yield *next;
console.log(last+"!")
}
function *m1(next){
var start = new Date;
console.log("start=======1111");
yield next;
console.log("end=======1111");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
return "last "
}
function *m2(next){
var start = new Date;
console.log("start=======2222");
yield next;
console.log("end=======2222");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
function bind(fn, context){
var args = [].slice.call(arguments, 2)
return function *(fn){
args.push(function (err, e){
console.log("+++++++++"+fn)
//yield e
})
console.log(args)
fn.apply(context, args)
}
}
function delay(time) {
return function(fn) {//这是一个普通函数,使用thunkToPromise
setTimeout(function() {
console.log("setTimeout")
fn(null, time)//null表示没有错
}, time)
}
}
function *m3(next){
var start = new Date;
console.log("start=======3333"+thunkify);
var txt = yield thunkify(fs.readFile)( './compose.js',"utf8")
console.log(txt);
yield next;
console.log("end=======3333");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
}
var list = [respond, m1, m2, m3]
var fn = co.wrap(compose(list));
fn.call({method: "get", url: "localhost"}).catch(function (err) {
console.error(err.stack);
});
串行与并行
var thunkify = require('thunkify');
var co = require('co');
var fs = require('fs');
var readFile = thunkify(fs.readFile);
co(function *() {
var now = new Date - 0
var a = yield readFile('./readme.md');
console.log(a.length)
var b = yield readFile('./fwp_core.zip');
console.log(b.length)
var c = yield readFile('./package.json');
console.log(c.length)
console.log(new Date - now)
})
var thunkify = require('thunkify');
var co = require('./co');
var fs = require('fs');
var readFile = thunkify(fs.readFile);
co(function *() {
var now = new Date - 0
var a = yield [readFile('./readme.md'),readFile('./fwp_core.zip'), readFile('./package.json')];
console.log(a[0].length, a[1].length, a[2].length)
console.log(new Date - now)
})
koa实现bigpipe的核心代码
var koa = require('koa');
var Readable = require('stream').Readable
var koa = require('koa');
var thunkify = require("thunkify")
var fs = require("fs")
var app = koa();
app.use(function *(){
this.type = 'text/html'
var stream = this.body = new Readable()
stream._read = function () {}
var readFile = thunkify(fs.readFile);
var a = yield [readFile('./start.html',"utf8"),readFile('./end.html',"utf8")];
stream.push(a[0] + '<br>');
setTimeout(function(){
stream.push('第二行<br>');
}, 1200)
setTimeout(function(){
stream.push(a[1] + '最后一行<br>');
stream.push(null)
}, 2200)
});
app.listen(3000);
http://www.bitscn.com/school/Javascript/201405/200167_2.html
index
https://github.com/koajs/bigpipe-example/tree/master/client https://gemnasium.com/npms/bigpipe-example http://www.jianshu.com/p/12cfa4ba29d4 https://github.com/undoZen/bigpipe-on-node The whole point is to split big task of generating entire HTML into many small tasks ( aka pagelets ). For example assume you want to send to a browser two things: user details and details of a transaction he just made. Instead of doing both these tasks ( ie loading data from DB ) and sending HTML after that, you would load a user from DB an send it to browser, then load transaction from DB and send it to browser.
This ultimetly leads to a better user experience and faster loading time ( because browser generates HTML chunk by chunk which is more efficient then everything at once ).
Note that this was developed ( or at least widely used by ) Facebook, so read this article for more info: