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

```js // 单个异步任务执行,如何使用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 { }, 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); ```

results matching ""

    No results matching ""