Skip to content

Latest commit

 

History

History
249 lines (196 loc) · 5.73 KB

23.Generator的执行细节分析.md

File metadata and controls

249 lines (196 loc) · 5.73 KB

Generator执行细节分析(co库底层实现原理)

// 单个异步任务执行,如何使用generator函数呢?

// v1: 创建一个generator函数
function* gen() {
    let url = 'http://www.baidu.com';
    let res = yield fetch(url);
    console.log(res)
}

// 1. 调用Generator函数,获取遍历器对象
let g = gen();
// 2. 使用next方法,执行异步任务的第一阶段,fetch(url)
let res = g.next();

// 3. res 是一个Promise对象,{ value: Promise { <pending> }, done: false }
res.value.then(data => {
    // 4. 将获取到的数据格式化
    return data.json();
}).then(data => {
    // 5. 然后将格式化之后的数据传进去,调用next函数,会执行输入console.log(res)
    g.next(data);
})


// v2: 如何执行下面的多个异步函数呢
function* gen() {
    // yiled实际上会返回一个Promise对象
    var r1 = yield fetch('https://api.github.com/users/github');
    var r2 = yield fetch('https://api.github.com/users/github/followers');
    var r3 = yield fetch('https://api.github.com/users/github/repos');

    console.log([r1.bio, r2[0].login, r3[0].full_name].join('\n'));
}

// 使用递归的方式来实现
function run(gen) {
    // 1. 执行自己定义的Generator函数
    let g = gen();
    function next(data) {
        let res = g.next(data);

        // 执行完毕
        if (res.done) return;

        res.value.then(data => {
            return data.json();
        }).then(data => {
            next(data)
        });

    }

    // 2. 执行next函数
    next();
}

// 首次执行并调用
run(gen);


// v3: 上面的代码优化
function run(gen) {
    // 1. 获取遍历器对象
    let g = gen();

    function next(data) {
        let res = g.next(data);

        if (res.done) return;

        res.value.then(data => {
            // 继续调用下一个Generator函数
            next(data);
        });
    }

    next();
}

// v4: 实现一个run函数,通用版本
function run(gen) {
    // 1. 获取遍历器对象
    let g = gen();


    // next函数
    function next(data) {
        let res = g.next(data);

        if (res.done) return;

        if (isPromise(res.value)) {
            // 如果是一个Promise对象的话
            res.value.then(data => {
                next(data);
            });
        }
        else {
            res.value(data);
        }

    }

    next();
}

// 可以判断一个对象是不是Promise对象
function isPromise(obj) {
    return typeof obj.then === 'function';
}


// v5. 上面代码继续优化
function run(gen) {
    let g = gen();

    return new Promise((resolve, reject) => {
        let res;
        function next(data) {
            try {
                res = g.next(data);
            }
            catch (e) {
                return reject(e);
            }

            if (res.done) return resolve(res.value);

            let val = toPromise(res.value);

            val.then(data => {
                next(data);
            }, err => {
                reject(err);
            });
        }

        next();
    });
}

// 可以转换任意对象到一个Promise
function toPromise(obj) {
    if (isPromise(obj)) {
        return obj;
    }
    else if ('function' === typeof obj) {
        return thunkToPromise(obj);
    }

    return obj;
}

// 把一个函数转换为Promise对象
function thunkToPromise(fn) {
    return new Promise((resolve, reject) => {
        fn((err, res) => {
            if (err) return reject(err);
            resolve(res);
        })
    });
}


// v6. 上面的代码进一步优化
function run(gen) {

    return new Promise(function(resolve, reject) {
        if (typeof gen == 'function') gen = gen();

        // 如果 gen 不是一个迭代器
        if (!gen || typeof gen.next !== 'function') return resolve(gen)

        onFulfilled();

        function onFulfilled(res) {
            var ret;
            try {
                ret = gen.next(res);
            } catch (e) {
                return reject(e);
            }
            next(ret);
        }

        function onRejected(err) {
            var ret;
            try {
                ret = gen.throw(err);
            } catch (e) {
                return reject(e);
            }
            next(ret);
        }

        function next(ret) {
            if (ret.done) return resolve(ret.value);
            var value = toPromise(ret.value);
            if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
            return onRejected(new TypeError('You may only yield a function, promise ' +
                'but the following object was passed: "' + String(ret.value) + '"'));
        }
    })
}

function isPromise(obj) {
    return 'function' == typeof obj.then;
}

function toPromise(obj) {
    if (isPromise(obj)) return obj;
    if ('function' == typeof obj) return thunkToPromise(obj);
    return obj;
}

function thunkToPromise(fn) {
    return new Promise(function(resolve, reject) {
        fn(function(err, res) {
            if (err) return reject(err);
            resolve(res);
        });
    });
}

module.exports = run;


// v7: co是什么呢?
// co 是大神 TJ Holowaychuk 于 2013 年 6 月发布的一个小模块,用于 Generator 函数的自动执行。
// yield 后是一个 Promise
var fetch = require('node-fetch');
var co = require('co');

function* gen() {
    var r1 = yield fetch('https://api.github.com/users/github');
    var json1 = yield r1.json();
    var r2 = yield fetch('https://api.github.com/users/github/followers');
    var json2 = yield r2.json();
    var r3 = yield fetch('https://api.github.com/users/github/repos');
    var json3 = yield r3.json();

    console.log([json1.bio, json2[0].login, json3[0].full_name].join('\n'));
}

// 可以让Generator里面的函数按顺序执行
co(gen);