简单封装JS下的异步任务对象
废话开篇:js有微任务跟宏任务,但是不管是哪种任务并不代表着会开辟新的线程,可以理解为将上述两种任务放到“主任务”之后执行,有点像iOS下的在主线程调用异步函数一样,其实也只是将异步任务降低了优先级,等主线程不忙的时候再处理该任务,比如:UI任务优先级较高,所以,任何的主线程下的异步任务都要排到UI任务结束之后进行。
一、利用 Promise 实现异步任务
利用 Promise 实现异步任务,如果不加任何限制,微任务的执行便会按添加的先后顺序进行执行。这里简单封装一下,实现微任务下的执行依赖。可能有人会问,await 不就行了吗?是的,声明异步方法就能满足,顺序执行。但是还是要整理一个过程来增加对代码的理解。
二、实现效果
这里简单的逻辑是:异步3任务 -> 异步2任务 -> 异步1任务
三、代码实现
下面是调度代码逻辑
function dispath(){
console.log('异步3完成,异步2才能开始,异步2完成,异步1才能开始')
//创建任务一
let blockOperationOne = new BlockOperation((b)=>{
console.log('异步1任务');
//单一任务完成事件通知
b.finished();
});
//添加新任务
blockOperationOne.addExecutionBlock((b)=>{
console.log('异步1新增任务');
b.finished();
});
//创建任务二
let blockOperationTwo = new BlockOperation((b)=>{
console.log('异步2任务');
b.finished();
});
blockOperationTwo.addExecutionBlock((b)=>{
console.log('异步2新增任务2');
b.finished();
});
blockOperationTwo.addExecutionBlock((b)=>{
console.log('异步2新增任务需要等待4秒');
setTimeout(() => {
console.log('异步2新增任务3');
b.finished();
}, 4000);
});
//创建任务三
let blockOperationThree = new BlockOperation((b)=>{
console.log('异步3需要等待1秒');
setTimeout(() => {
console.log('异步3任务');
b.finished();
}, 1000);
});
//添加依赖
blockOperationOne.addDependency(blockOperationTwo);
blockOperationTwo.addDependency(blockOperationThree);
//开始执行
blockOperationOne.start();
blockOperationTwo.start();
blockOperationThree.start();
console.log('我是宏任务');
};
下面是封装的 BlockOperation 对象
//任务通知对象
class Block{
//完成回调
doneCallBack = null
constructor(doneCallBack){
this.doneCallBack = doneCallBack
}
//单任务完成
finished(){
this.doneCallBack();
}
}
//任务对象
class BlockOperation {
//任务集合
blocks = []
//是否已开始
isBeginStart = false
//依赖任务对象
dependencyOperation = null
//被依赖影响的对象
impactOperation = null
//全部完成的事件判定
allOperationIsDone = false
//全部完成的事件回调
allOperationDoneBlock = ()=>{
//将依赖任务完成进行自身任务
if(this.impactOperation){
this.impactOperation.start()
}
};
//代理
proxy = null;
//代理set方法
handler = {
set(target,property,value){
target[property] = value
if(property == 'allOperationIsDone' && value){
//执行闭包回调
target.allOperationDoneBlock();
}
return true
},
get(target,property){
return target[property]
}
}
//初始化
constructor(cb){
this.blocks.push(cb)
this.addObserver()
}
//添加观察者
addObserver(){
this.proxy = new Proxy(this, this.handler)
}
//添加新任务
addExecutionBlock(cb){
this.blocks.push(cb)
}
//添加依赖
addDependency(blockOperation){
this.dependencyOperation = blockOperation
blockOperation.impactOperation = this
}
//开始异步任务
start(){
const self = this
self.isBeginStart = true
//先判断是否有依赖
if(this.dependencyOperation && this.dependencyOperation.allOperationIsDone == false){
//这里加一个定时器(目的是添加一个相对靠后的宏任务来检查所有的任务是否都执行了开启,不是很严谨)
setTimeout(()=>{
if(self.dependencyOperation.isBeginStart == false){
throw Error('请检查是否有依赖任务未开启')
}
},0)
//等待依赖的执行完
return
}
self.blocks.forEach((operationBlock) => {
Promise.resolve(operationBlock).then((res)=>{
if(res){
res(new Block(()=>{
//删除任务
delete self.blocks[self.blocks.indexOf(res)]
//过滤null(防止delete执行后数组中有null占位)
self.blocks = self.blocks.filter((item) => {
return item
})
//判断是否全部完成
self.proxy.allOperationIsDone = (self.blocks.length == 0)
}))
};
})
});
}
}
四、总结与思考
其实 async / await 的使用是可以避免回调的,但是,这里并不是去用它的特性来优化代码,而是用 Promise 的 then 函数是微任务来处理异步,目的就是将一些不太重要的逻辑放到主任务之后,这里也是参考了 iOS 下的 NSBlockOperation 的一些 api 命名,语言间实现代码虽然不同,但却存在着互通的设计思路吧。代码拙劣,大神勿笑[抱拳][抱拳][抱拳]
转载自:https://juejin.cn/post/7166532398237089799