# 写在前面

最近发现了一个叫 co 的神奇东西,似乎是 es7 没发布时,没有 async,await 时的一个非常好用的库,感觉挺好玩的,打算模拟实现一个。

# 实现过程

# 只能执行同步代码版

这一版的代码十分简单,主要做到三点就行了

  • 把返回的 promise 的 resolve 函数一层层往下传,当迭代结束时,把生成器函数的返回值传给 resolve 函数
  • 如果没有结束迭代,就把上一次迭代获得的值作为下一次 next 函数执行的参数传入,一直重复执行直到迭代结束为止
function run(generatorFunc, data) {
    // 获得生成器
    const generator = generatorFunc(data);
    // 执行 next 方法获取结果
    let result = execute(generator);
    return new Promise((resolve, reject) => {
        // 处理结果
        handleResult(generator, result, resolve, reject);
    });
}
function handleResult(generator, result, resolve, reject) {
    // 如果 result.done 是真,证明已经迭代完了,而且第一次为 true 时的 value 是生成器函数的返回值
    // 所以我们通过 resolve 可以把返回值传给 run 函数外的 then
    if (result.done){
        resolve(result.value);
        return;
    }
    // 如果没有结束,就继续执行,同时把上一个 next 返回的 value 传回去
    result = execute(generator, result.value);
    // 这里的 generator, resolve, reject 都是一层一层往下传不会变的
    // 因为要执行的是同一个生成器,要接收返回值和错误原因的也是同一个函数,都是要传给 run 函数返回的 promise 绑定的 then
    handleResult(generator, result, resolve, reject);
}
// 就是执行 next 方法
function execute(generator, data) {
    return generator.next(data);
}
function run(generatorFunc, data) {
    // 获得生成器
    const generator = generatorFunc(data);
    // 执行 next 方法获取结果
    let result = execute(generator);
    return new Promise((resolve, reject) => {
        // 处理结果
        handleResult(generator, result, resolve, reject);
    });
}
function handleResult(generator, result, resolve, reject) {
    // 如果 result.done 是真,证明已经迭代完了,而且第一次为 true 时的 value 是生成器函数的返回值
    // 所以我们通过 resolve 可以把返回值传给 run 函数外的 then
    if (result.done){
        resolve(result.value);
        return;
    }
    // 如果没有结束,就继续执行,同时把上一个 next 返回的 value 传回去
    result = execute(generator, result.value);
    // 这里的 generator, resolve, reject 都是一层一层往下传不会变的
    // 因为要执行的是同一个生成器,要接收返回值和错误原因的也是同一个函数,都是要传给 run 函数返回的 promise 绑定的 then
    handleResult(generator, result, resolve, reject);
}
// 就是执行 next 方法
function execute(generator, data) {
    return generator.next(data);
}

测试代码

run(function * () {
    let name = yield "sena";
    let age = yield 16;
    return {
        name,
        age
    }
}).then((data) =>; {
    console.log(data);    //  { name: 'sena', age: 16 }
}, (err) =>; {
    console.log("error:",err);
});

# 能执行异步代码版

这次的代码也不难,主要是增加了处理执行 next 方法时返回值是一个 promise 的逻辑,大致思路是,把执行下次 next 的代码放到返回 promise 的 then 中,也就是当返回的 promise 执行了 resolve 方法,才会触发 then 中绑定的 generator.next () 方法

function run(generatorFunc, data) {
    // 获得生成器
    const generator = generatorFunc(data);
    // 执行 next 方法获取结果
    let result = execute(generator);
    return new Promise((resolve, reject) => {
        // 处理结果
        handleResult(generator, result, resolve, reject);
    });
}
function handleResult(generator, result, resolve, reject) {
    // 如果 result.done 是真,证明已经迭代完了,而且第一次为 true 时的 value 是生成器函数的返回值
    // 通过 resolve 可以把返回值传给 run 函数外的 then
    if (result.done){
        resolve(result.value);
        return;
    }
    // 如果值是个 promise
    if (isPromise(result.value)){
        // 把 next () 放到 then 里面执行
        result.value.then((data) => {
            result = execute(generator, data);
            handleResult(generator, result, resolve, reject);
        })
    } else {
        // 如果没有结束,就继续执行,同时把上一个 next 返回的 value 传回去
        result = execute(generator, result.value);
        // 这里的 generator, resolve, reject 都是一层一层往下传不会变的
        // 因为要执行的是同一个生成器,要接收返回值和错误原因的也是同一个函数,都是要传给 run 函数返回的 promise 绑定的 then
        handleResult(generator, result, resolve, reject);
    }
}
// 就是执行 next 方法
function execute(generator, data) {
    return generator.next(data);
}
// 用于判断返回值是不是 promise
function isPromise(obj) {
    return 'function' == typeof obj.then;
}

测试代码

function * requestData() {
    let name = yield api1();
    console.log(name);      // 1s 后输入 "sena"
    let age = yield api2();
    console.log(age);       // 2s 后输出 16
    return {
        name,
        age
    };
}
function api1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("sena");
        }, 1000);
    })
}
function api2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(16);
        }, 1000);
    })
}
run(requestData).then((res) => {
    console.log(res);    // 2s 后输出 {name: 'sena', age: 16}
});

加入错误处理

到这里代码就开始有意思起来了,主要是实现下面两点

  • 用 try catch 捕获异步的异常
  • 如果没有 try catch 就执行 reject 方法

这里我们会用到 generator.throw 方法,用于把 reject 的情况转化成 error 传给生成器内的 try catch

function run(generatorFunc, data) {
    // 获得生成器
    const generator = generatorFunc(data);
    // 执行 next 方法获取结果
    let result = execute(generator);
    return new Promise((resolve, reject) => {
        // 处理结果
        handleResult(generator, result, resolve, reject);
    });
}
function handleResult(generator, result, resolve, reject) {
    // 如果 result.done 是真,证明已经迭代完了,而且第一次为 true 时的 value 是生成器函数的返回值
    // 通过 resolve 可以把返回值传给 run 函数外的 then
    if (result.done){
        resolve(result.value);
        return;
    }
    // 如果值是个 promise
    if (isPromise(result.value)){
        // 把 next () 放到 then 里面执行
        result.value.then((data) => {
            result = execute(generator, data);
            handleResult(generator, result, resolve, reject);
        }, (err) =>
        // 当返回的 promise 执行 reject 时
        {
            // 在外面包一层 try catch, 如果生成器函数里没有写 try catch, 就会在这里捕获 error
            try {
                // 把错误抛入生成器
                result = throwError(generator, err);
                // 如果错了 try catch 就继续处理
                handleResult(generator, result, resolve, reject);
            }catch (e) {
                // 如果在这里接收到错误,证明生成器里的错误没有被捕获,执行绑定的 reject
                reject(e);
            }
        })
    } else {
        // 如果没有结束,就继续执行,同时把上一个 next 返回的 value 传回去
        result = execute(generator, result.value);
        // 这里的 generator, resolve, reject 都是一层一层往下传不会变的
        // 因为要执行的是同一个生成器,要接收返回值和错误原因的也是同一个函数,都是要传给 run 函数返回的 promise 绑定的 then
        handleResult(generator, result, resolve, reject);
    }
}
// 就是执行 next 方法
function execute(generator, data) {
    return generator.next(data);
}
// 用于把错误抛入生成器
function throwError(generator, error) {
    return generator.throw(error);
}
// 用于判断返回值是不是 promise
function isPromise(obj) {
    return 'function' == typeof obj.then;
}

测试代码

function * requestData() {
    let name = yield api1();
    console.log(name);      // 1s 后输入 "sena"
    let age = yield api2();
    console.log(age);       
    return {
        name,
        age
    };
}
function api1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("sena");
        }, 1000);
    })
}
function api2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("api2炸了");
        }, 1000);
    })
}
run(requestData).then((res) => {
    console.log(res);
}, (err) => {
    console.log("错误原因 : " + err);    // 错误原因 : api2 炸了
});

还是测试代码

function * requestData() {
    let name = yield api1();
    console.log(name);      // 1s 后输入 "sena"
    let age;
    try {
        age = yield api2();
        console.log(age);
    }catch (e) {
        console.log("err:" + e);      //err:api2 炸了 
    }
    return {
        name,
        age
    };
}
function api1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("sena");
        }, 1000);
    })
}
function api2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("api2炸了");
        }, 1000);
    })
}
run(requestData).then((res) => {
    console.log(res);        // { name: 'sena', age: undefined }
}, (err) => {
    console.log("错误原因 : " + err);
});

好了,一个简单的仿 co 自动执行函数就写完了,如果有遗漏或错误,欢迎指出