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

1,205 阅读3分钟

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

1.Generator

  • 当你在执行一个函数的时候,你可以在某个点暂停函数的执行,并且做一些其他工作,然后再返回这个函数继续执行, 甚至是携带一些新的值,然后继续执行。
  • 上面描述的场景正是JavaScript生成器函数所致力于解决的问题。当我们调用一个生成器函数的时候,它并不会立即执行, 而是需要我们手动的去执行迭代操作(next方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器会遍历每个中断点。
  • next 方法返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,这是向 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])
        }
    });
}

是不是迫不及待的想知道promise是如何实现的?下一节我们来Promise的实现原理,喜欢的点个赞吧^_^! 支持我的可以给我打赏哈!