代码
let Sequence = function(){
let i = -1, args = arguments, l = args.length;
(function lambda(){
return new Promise((next)=>{
i ++;
if(i<l) args[i](next);
}).then(lambda);
})();
}
用法
Sequence(
n=>{
// ...
n();
},
n=>{
//...
n();
}
)
实现过程:
第1步
理解promise,仅传入resolve,以减少不必要的干扰。
let p1 = new Promise((resolve)=>{
resolve(1);
});
p1.then(console.log);
let p2 = new Promise((resolve)=>{
resolve(1);
});
p2.then( (i)=>{
console.log(100*i)
});
第2步:
理解promise对回调地狱的优化。
new Promise((resolve)=>{
let e = 1;
resolve(e);
}).then(
(e)=>{
return new Promise((resolve)=>{
console.log(e);
resolve(e+1);
});
}).then(
(e)=>{
return new Promise((resolve)=>{
console.log(e);
resolve(e+1);
});
}).then(console.log);
第3步
抽象代码,精简主流程。
function promiseCount(e){
return new Promise((resolve)=>{
console.log(e);
resolve(e+1);
});
}
new Promise((resolve)=>{
resolve(1);
}).then(
promiseCount
).then(
promiseCount
).then(console.log);
第4步
编写执行器,把setTimeout作为测试用异步事件。执行器内部设置index作 “辅助指针” 指向当前递归事件。
let eventArray = [
(callback)=>{
console.log(1);
$.ajax({
url,
success(){
callback();
}
})
},
(callback)=>{
console.log(2);
setTimeout(callback,100);
},
(callback)=>{
console.log(3);
setTimeout(callback,100);
},
(callback)=>{
console.log(4);
setTimeout(callback,100);
},
(callback)=>{
console.log(5);
setTimeout(callback,100);
}
];
function Sequence(eventArray){
let index = -1;
(function lambda(){
return new Promise((resolve)=>{
index ++; // 指向队列中的下一个事件
if(index<eventArray.length){
eventArray[index](resolve); // 把resolve交由当前事件的回调函数处理,即当前事件执行完之后就会执行then中新的lambda,得到的效果是“同步”
}
}).then(lambda);
})();
}
Sequence(eventArray);
第5步 (选看)
增加数据收集器
let eventArray = [
(callback)=>{
console.log(1);
setTimeout(function(){
callback().collector.z=0;
callback().resolve();
},100);
},
(callback)=>{
console.log(2);
setTimeout(function(){
callback().collector.a=1;
callback().resolve();
},100);
},
(callback)=>{
console.log(3);
setTimeout(function(){
callback().collector.b=2;
callback().resolve();
},100);
},
(callback)=>{
console.log(4);
setTimeout(function(){
callback().collector.c=3;
callback().resolve();
},100);
},
(callback)=>{
console.log(5);
setTimeout(function(){
callback().collector.d=4;
callback().resolve();
},100);
}
];
let Sequence = function(eventArray){
let index = -1, events = eventArray;
let collector = {};
(function lambda(){
return new Promise((resolve)=>{
index ++;
if(index<events.length){
events[index](()=>{
return {
resolve,
collector,
}
});
}
}).then(lambda);
})();
this.getData = function(){
return collector;
}
this.clear = function(){
collector = null;
}
}
let ev = new Sequence(eventArray);
setTimeout(function(){
console.log(ev.getData())
},2000);
collector会一直存在ev对象的内部,仅能通过调用getData获取。当然还可以往Sequence里添加更多方法。
第6步
提炼核心, 并把 resolve 改为 next.
let Sequence = function(eventArray){
let index = -1;
(function lambda(){
return new Promise((next)=>{
index ++;
if(index<eventArray.length){
eventArray[index](next);
}
}).then(lambda);
})();
}
new Sequence([
(next)=>{
setTimeout(()=>{
// do something
console.log(1);
next();
},100);
},
(next)=>{
setTimeout(()=>{
// do something
console.log(2);
next();
},100);
},
(next)=>{
setTimeout(()=>{
// do something
console.log(3);
next();
},100);
},
(next)=>{
setTimeout(()=>{
// do something
console.log(4);
next();
},100);
},
(next)=>{
setTimeout(()=>{
// do something
console.log(5);
next();
},100);
}
]);
最后一步
再次精简代码并提炼写法,减少调用键入次数。
let Sequence = function(){
let i = -1, args = arguments, l = args.length;
(function lambda(){
return new Promise((next)=>{
i ++;
if(i<l) args[i](next);
}).then(lambda);
})();
}
new Sequence(
next=>{
setTimeout(()=>{
// do something
console.log(1);
next();
},100);
},
next=>{
setTimeout(()=>{
// do something
console.log(2);
next();
},100);
},
next=>{
setTimeout(()=>{
// do something
console.log(3);
next();
},100);
},
next=>{
setTimeout(()=>{
// do something
console.log(4);
next();
},100);
},
next=>{
setTimeout(()=>{
// do something
console.log(5);
next();
},100);
}
);
最终结果
let Sequence = function(){
let i = -1, args = arguments, l = args.length;
(function lambda(){
return new Promise((next)=>{
i ++;
if(i<l) args[i](next);
}).then(lambda);
})();
}
发布于 NPM, 安装:npm i zfc-sequence
最终用法
let sequence = require('zfc-sequence');
sequence(
next=>{
setTimeout(()=>{
// ...
next();
},1000)
},
next=>{
setTimeout(()=>{
// ...
next();
},1000)
},
next=>{
setTimeout(()=>{
// ...
next();
},1000)
},
next=>{
setTimeout(()=>{
// ...
next();
},1000)
}
);
点个赞👍实现得很精髓.
if(i<l) args[i](next);
// 改成 l < args.length
// 是不是就能动态添加任务,只要队列没有结束
if(i<args.length) args[i](next);
@axetroy 如果可以再补充。
怎么for循环添加任务
抽象过程不错,很久以前也写过两个类似的: https://www.npmjs.com/package/fn-sequence https://www.npmjs.com/package/serial-async
实现的不错,那这个和.then里面返回promise,再点then,功能上有什么区别呢? 比如
new Promise((reslove,rej)=>{
setTimeout(()=>{
reslove(1);
},1000)
}).then((res)=>{
console.log(res)
return new Promise((reslove,rej)=>{
setTimeout(()=>{
reslove(2);
},1000)
});
}).then((res)=>{
console.log(res)
})
666
666 在嵌入式领域,这个叫“状态机”,很棒的程序思路
@zy445566 理解第一步和第三步,并要明白 next 拿到外部调用,实际上指向相同内存。
已有的实现比较好的库可以参考 https://github.com/caolan/async
@axetroy 动态添加事件补充, 不可避免地写成三角形。 总会找到更好的写法, async很好了, 感觉是扩展了底层? 而动态添加事件的应用场景在哪里?
let Sequence = function(){
let i = -1, args = arguments;
(function lambda(){
return new Promise((next)=>{
i ++;
if(i<args.length) args[i]({
next,
push(){
Array.prototype.push.call(args,...arguments);
}
});
}).then(lambda);
})();
}
let r = 0;
Sequence(
o=>{
console.time('test');
setTimeout(()=>{
console.log(r++);
o.push(
o=>{
setTimeout(()=>{
console.log('动态添加的第一事件');
o.next();
})
},
o=>{
setTimeout(()=>{
console.log('动态添加的第二事件');
o.next();
})
}
)
o.next();
})
},
o=>{
setTimeout(()=>{
console.log(r++);
o.push(o=>{
setTimeout(()=>{
console.log('动态添加的第三事件');
o.next();
})
})
o.next();
})
},
o=>{
setTimeout(()=>{
console.log(r++);
o.push(o=>{
setTimeout(()=>{
console.log('动态添加的第四事件');
o.next();
})
})
o.next();
})
}
)
@zhhb async 不是对 generator 的封装吗?
@DoubleCG 以前我写怕从的时候,也封装过类似的库。
大概是这样: 你不知道爬取的数据有多少条 (任务数量未知), 只能发现一条就 push 一条 (动态添加)
然后还保持任务池内的任务,最多并发 x 个任务…