对generator不熟悉的可以先看下相关的基础资料
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator
在早些时候,generator + promise 可以比较优雅的解决JS中较为头痛的回调嵌套问题,并且为了解决让 generator 能够自动执行的问题,出现了诸如co这样的模块,在这里自己简单的实现一版,探索一下其中的原理,首先要解决几个问题。
-
自执行
自执行的关键就是要在每一个异步的回调中,去调用iterator的next方法,并且传入相应回调的参数。用递归的思路可以较好解决,这里要注意的是,yield 后面可以跟上同步操作,这种情况我也统一包装成了promise返回。 -
yield 后面跟的是一个promise数组
当有多个异步任务时,这里会利用Promise.all 来处理这个情况,最终返回一个结果数组,注意下 promise 数组的判断就好。 -
在这个自执行方法执行完后,需要返回一个promise,可以继续调用then方法,这个只需要在最开始返回一个promise即可。
示例如下
function run (genFn) {
let it = genFn();
let store = it.next();
return new Promise((resolve,reject)=>{
function next () {
if (store.done) resolve(store.value);
// 如果是promise数组
if (isPromiseArray(store.value)){
Promise.all(store.value).then((retArray)=>{
store = it.next(retArray);
next();
}).catch(e => reject(e.message));
} else {
// 因为每一个步骤可能是异步,也可能是同步,这里统一转化为Promise来处理
if (!(store.value instanceof Promise)) store.value = Promise.resolve(store.value).catch((e => console.log(e.message)));
store.value.then((ret) => {
store = it.next(ret);
next();
}).catch(e => reject(e.message));
}
}
function checkType(variable){
return Object.prototype.toString.call(variable).slice(8,-1).toLowerCase();
}
function isPromiseArray (array) {
if (!(checkType(array) === 'array')) return false
let flag = true;
for (let i=0; i<array.length;i++) {
if (!(array[i] instanceof Promise)){
flag = false;
break;
}
}
return flag;
}
next();
});
}