JS异步解决方案的发展流程(二) #

我们发现Promise已经可以解决了异步编程问题,但是仍然不够优雅,我们更希望编写异步代码能够像同步代码一样简洁。

1.Generator #

1.1 generator配合promise #

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库。

1.2 应用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库会帮我们进行自动迭代。解决了需要手动迭代的麻烦

1.3 模拟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()
    });
}

2.async/await #

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的库。

3.Q库 #

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);
});

3.1 Q.all方法 #

与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)
})

3.2 Q.fcall #

可以通过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)
})

4.blueBird #

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方法。

4.1 实现bluebird #

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])
        }
    });
}