我们发现Promise已经可以解决了异步编程问题,但是仍然不够优雅,我们更希望编写异步代码能够像同步代码一样简洁。
暂停函数
的执行,并且做一些其他工作,然后再返回这个函数继续执行, 甚至是携带一些新的值
,然后继续执行。手动的去执行迭代操作
(next方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器会遍历每个中断点。let fs = require('fs');
function read(file){
return new Promise(function(resolve,reject){
fs.readFile(file,'utf8',function(err,data){
if(err) return reject(err);
resolve(data);
})
})
}
function *gen(filename){
// 第一次it.next().value 就是yield后面的promise
let a = yield read(filename);
let b = yield read(a);
return b;
}
let it = gen('./1.txt');
// 这里返回迭代器(迭代器返回一个对象拥有value和done属性,当迭代完成后done的属性为true)
it.next().value.then(function(data){
it.next(data).value.then(function(value){
console.log(it.next(value).value);
})
})
这里我们手动迭代发现很复杂,所以有一个库可以配合Generator使用,就是我们的co库。
function *gen(filename){
let a = yield read(filename);
let b = yield read(a);
return b;
}
let co = require('co');
co(gen('./1.txt')).then(function(data){
console.log(data);
},function(err){
console.log(err)
})
co库会帮我们进行自动迭代。解决了需要手动迭代的麻烦
function co(gen){
return new Promise(function(resolve,reject){
let it = gen;
function next(data){ // 下一次迭代时将上一次的结果传递进去
let {value,done} = it.next(data);
if(!done){
value.then(function(data){
next(data); // 如果没完成继续迭代
},reject)
}
else{
resolve(data);
}
}
next()
});
}
async和await就是generator和co的语法糖,使用async关键字,你可以轻松地达成之前使用生成器和co函数所做到的工作
let fs = require('fs');
function read(file){
return new Promise(function(resolve,reject){
fs.readFile(file,'utf8',function(err,data){
if(err) return reject(err);
resolve(data);
})
})
}
async function gen(filename){
let a = await read(filename);
let b = await read(a);
return b;
}
gen('./1.txt').then(function(data){
console.log(data)
},function(err){
console.log(err);
});
我们发现无论是generator还是async/await都离不开promise,我们在介绍几个有关promise的库。
Q是一个在Javascript中实现promise的模块
let fs = require('fs');
let Q = require('q');
function read(){
let defer = Q.defer(); //我们的延迟对象
fs.readFile('1.txt','utf8',function(err,data){
if(err) return defer.reject(err);
defer.resolve(data);
});
return defer.promise;
}
read().then(function(data){
console.log(data);
},function(err){
console.log(err);
});
与Promise.all中的用法一致
let fs = require('fs');
let Q = require('q');
function read(filename){
let defer = Q.defer(); //我们的延迟对象
fs.readFile(filename,'utf8',function(err,data){
if(err) return defer.reject(err);
defer.resolve(data);
});
return defer.promise;
}
Q.all([read('./1.txt'),read('./2.txt')]).then(function([a,b]){
console.log(a,b)
});
// 这里我们也可以利用spread将结果展开
Q.all([read('./1.txt'),read('./2.txt')]).spread(function(a,b){
console.log(a,b)
})
可以通过Q.fcall创造出promise
let fs = require('fs');
let Q = require('q');
Q.fcall(function(){
return 100;
}).then(function(data){
console.log('result',data)
},function(err){
console.log(err)
})
blueBird中有两个常用的方法一个叫promisify另一个叫promisifyAll
let fs = require('fs');
let blueBird = require('bluebird');
let read = blueBird.promisify(fs.readFile);
read('./1.txt','utf8').then(function(data){
console.log(data)
},function(err){
console.log(err)
});
// 可以将异步方法promise化,同样也可以将所有的方法promise化,通过promisifyAll后需要在原有的函数后加上Async后缀
let fs = require('fs');
let blueBird = require('bluebird');
blueBird.promisifyAll(fs);
fs.readFileAsync('./1.txt','utf8').then(function(data){
console.log(data)
},function(err){
console.log(err)
});
node也已经在util模块中实现了promisify方法。
function promisify(fn) {
return function (...args) {
return new Promise(function (resolve, reject) {
fn.call(null, ...args, function (err, data) {
if (err) return reject(err);
resolve(data);
})
})
}
}
function promisifyAll(obj){
Object.keys(obj).forEach(item=>{
if(typeof obj[item] === 'function'){
obj[item+'Async'] = promisify(obj[item])
}
});
}