写在前面
最近发现了一个叫co的神奇东西,似乎是es7没发布时,没有async,await时的一个非常好用的库,感觉挺好玩的,打算模拟实现一个。
实现过程
只能执行同步代码版
这一版的代码十分简单,主要做到三点就行了
- 把返回的promise的resolve函数一层层往下传,当迭代结束时,把生成器函数的返回值传给resolve函数
- 如果没有结束迭代,就把上一次迭代获得的值作为下一次next函数执行的参数传入,一直重复执行直到迭代结束为止
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function run(generatorFunc, data) { const generator = generatorFunc(data); let result = execute(generator); return new Promise((resolve, reject) => { handleResult(generator, result, resolve, reject); }); } function handleResult(generator, result, resolve, reject) { if (result.done){ resolve(result.value); return; } result = execute(generator, result.value); handleResult(generator, result, resolve, reject); }
function execute(generator, data) { return generator.next(data); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function run(generatorFunc, data) { const generator = generatorFunc(data); let result = execute(generator); return new Promise((resolve, reject) => { handleResult(generator, result, resolve, reject); }); } function handleResult(generator, result, resolve, reject) { if (result.done){ resolve(result.value); return; } result = execute(generator, result.value); handleResult(generator, result, resolve, reject); }
function execute(generator, data) { return generator.next(data); }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12
| run(function * () { let name = yield "sena"; let age = yield 16; return { name, age } }).then((data) =>; { console.log(data); }, (err) =>; { console.log("error:",err); });
|
能执行异步代码版
这次的代码也不难,主要是增加了处理执行next方法时返回值是一个promise的逻辑,大致思路是,把执行下次next的代码放到返回promise的then中,也就是当返回的promise执行了resolve方法,才会触发then中绑定的generator.next()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| function run(generatorFunc, data) { const generator = generatorFunc(data); let result = execute(generator); return new Promise((resolve, reject) => { handleResult(generator, result, resolve, reject); }); } function handleResult(generator, result, resolve, reject) { if (result.done){ resolve(result.value); return; } if (isPromise(result.value)){ result.value.then((data) => { result = execute(generator, data); handleResult(generator, result, resolve, reject); }) } else { result = execute(generator, result.value); handleResult(generator, result, resolve, reject); } }
function execute(generator, data) { return generator.next(data); }
function isPromise(obj) { return 'function' == typeof obj.then; }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function * requestData() { let name = yield api1(); console.log(name); 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(() => { resolve(16); }, 1000); }) } run(requestData).then((res) => { console.log(res); });
|
加入错误处理
到这里代码就开始有意思起来了,主要是实现下面两点
- 用try catch捕获异步的异常
- 如果没有try catch就执行reject方法
这里我们会用到generator.throw方法, 用于把reject的情况转化成error传给生成器内的try catch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| function run(generatorFunc, data) { const generator = generatorFunc(data); let result = execute(generator); return new Promise((resolve, reject) => { handleResult(generator, result, resolve, reject); }); } function handleResult(generator, result, resolve, reject) { if (result.done){ resolve(result.value); return; } if (isPromise(result.value)){ result.value.then((data) => { result = execute(generator, data); handleResult(generator, result, resolve, reject); }, (err) => { try { result = throwError(generator, err); handleResult(generator, result, resolve, reject); }catch (e) { reject(e); } }) } else { result = execute(generator, result.value); handleResult(generator, result, resolve, reject); } }
function execute(generator, data) { return generator.next(data); }
function throwError(generator, error) { return generator.throw(error); }
function isPromise(obj) { return 'function' == typeof obj.then; }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function * requestData() { let name = yield api1(); console.log(name); 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); });
|
还是测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function * requestData() { let name = yield api1(); console.log(name); let age; try { age = yield api2(); console.log(age); }catch (e) { console.log("err:" + e); } 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); });
|
好了,一个简单的仿co自动执行函数就写完了,如果有遗漏或错误,欢迎指出