Q库generator和co库

Promise模式的q库

promise 是一种让异步代码写起来更舒服的模式。
q库可运行node下,它是一种promise的实现
npm i q 来安装它

下面采用回调方式编写

1
2
3
4
5
6
7
8
9
10
11
12
var fs = require('fs');
fs.readdir('.', function(err, rs){
fs.readFile(rs[0], function(err, f1){
console.log(f1);
fs.readFile(rs[1], function (err, f2) {
console.log(f2);
fs.readFile(rs[2], function (err, f3) {
console.log(f3);
})
})
})
})

采用q库的解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Q = require('q'),
fs = require('fs');

Q.nfcall(fs.readdri, '.').then(function(ns){
var promises = [];
ns.forEach(function (filename) {
promises.push(Q.ncall(fs.readFile, filename, 'utf-8'));
});
Q.all(promises).then(function(results){
console.log(results);
})
})
.fail(function(err){
console.log(err);
})

通过Q.ncall方法调用异步方法,返回一个promise对象,promise对象具有一个then方法,
可以运行得到结果。
这里var promises = []; 变量用于存储promise数组,把这个参数加入Q.all中,会得到一个promise对象,调用then方法会得到一个数组,这个数组就是所有promise运行的结果。
一切都围绕promise对象,它有一个then方法用于返回回调函数的结果,还有一个fail函数,用于返回异常对象,then 和fail不会被同时调用,就好比一个普通函数,如果抛出异常就不会有返回值一样。

then代表无异常情况下的返回值,fail表示抛出异常,如果过程中有任何异常,promise.then的函数就不会被调用,而是会调用fail,表示抛出异常。


generator

  • 运行generator代码需要node version>4
    1
    2
    3
    function * f(){}
    function *f() {}
    function* f() {}

以上三种方式都对

1
f.constructor; //function GeneratorFunction(){native code}

调用生成器函数就会产生一个生成器对象,生成器对象拥有next()方法,用来迭代

1
2
3
4
function * f() {
console.log('hello');
}
f();

运行代码后,终端没有打印出“hello”, 这是因为调用生成器函数产生一个生成器对象,但是不会执行里面的代码,需要调用next()函数.

1
2
3
4
5
6
function * f() {
console.log('hello');
}
f();
var g = f();
g.next();

g是一个gnerator对象,调用next方法后,将真正进入f的函数体。
根据上面的例子,会发现generator有个断点效果,就是在执行方法体之前, 等待next方法。
通常generator函数和yeild关键词一起使用。yeild可以理解为一个断点

1
2
3
4
5
6
7
function * gf() {
console.log("start");
var str = yield "hello";
console.log("result: ", str);
}
var g = gf();
g.next();

调用一次next,进入函数,打印出start,这时遇到yied所以中断了,我们后面继续一个next方法
打印出

1
2
3
g.next()
==> start
==> result: undefined

这好像和我想的不一样,第一感觉应该 result: hello 而不是result: undefined, 这是因为yield后面跟着的语句确实返回了, 只不过返回的hello值不是给了str变量,而是在第一次调用g.next(),它会返回一个对象({ value: ‘hello’, done: false }), 这个对象里有个value属性, 这个属性的值就是hello

so change the code

1
2
3
4
5
6
7
8
function * gf() {
console.log("start");
var str = yield "hello";
console.log("result: ", str);
}
var g = gf();
var value = g.next().value;
console.log("yield value-> ", value);

第一次调用next后,进入函数体, 遇到yield对象,中断了,就是: 中断并把yield的右侧的值, 写入本次调用next生成的对象中, 这个对象的value值就是右侧的值。
第二次调用next方法后,yield会把next(args)方法的args值赋予左侧变量, 并继续执行语句,下面的代码是我们想要的结果

1
2
3
4
5
6
7
8
function * gf() {
console.log("start");
var str = yield "hello";
console.log("result: ", str);
}
var g = gf();
var value = g.next().value;
g.next(value);

Then we get the result what we want…

1
2
start
result: hello

next 方法返回的对象有两个值{value, done}, value 表示yeild右侧的值也可能是generator的返回值; done如果为false,表示后面还有未执行的yeild语句。


CO库的介绍

co 用于优化异步函数调用方式, 支持promise和thunkify

npm i co thunkify

不采用co的代码:

1
2
3
4
5
6
7
8
9
10
11
12
var fs = require('fs');
fs.readdir('.', function(err, rs){
fs.readFile(rs[0], function(err, f1){
console.log(f1);
fs.readFile(rs[1], function (err, f2) {
console.log(f2);
fs.readFile(rs[2], function (err, f3) {
console.log(f3);
})
})
})
})

查看当前目录下有多少文件, 然后逐个读取, 把读取的数据打印到终端, 可笑的是我必须知道,这个目录下有多少文件。。。。

- 留有问题。。。

co 顺序执行下列代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var co = require('co'),
thunkify = require('thunkify');

function fun(time, callback){
setTimeout(function () {
callback(null, "setTimeout time is - " + time);
}, time);
}

var func = thunkify(fun);

co(function * () {
console.log(yield func(2000));
console.log(yield func(1000));
console.log(yield func(5000));
}).then(function () {
console.log('ok');
}).catch((err) => {
console.log(err);
});