废话开篇: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 = nullconstructor(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] = valueif(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 = blockOperationblockOperation.impactOperation = this}//开始异步任务start(){const self = thisself.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 命名,语言间实现代码虽然不同,但却存在着互通的设计思路吧。代码拙劣,大神勿笑[抱拳][抱拳][抱拳]
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取