探究一道价值25k的蚂蚁金服异步串行面试题
前言
朋友去面试蚂蚁金服,遇到了一道面试题,乍一看感觉挺简单的,但是实现起来发现内部值得一提的点还是挺多的。
先看题目:
constdelay=(ms)=>newPromise((resolve)=>setTimeout(resolve,ms)); constsubFlow=createFlow([()=>delay(1000).then(()=>log("c"))]); createFlow([ ()=>log("a"), ()=>log("b"), subFlow, [()=>delay(1000).then(()=>log("d")),()=>log("e")], ]).run(()=>{ console.log("done"); }); //需要按照a,b,延迟1秒,c,延迟1秒,d,e,done的顺序打印
按照上面的测试用例,实现createFlow:
- flow是指一系列effects组成的逻辑片段。
- flow支持嵌套。
- effects的执行只需要支持串行。
分析
先以入参分析,createFlow接受一个数组作为参数(按照题意里面的每一项应该叫做effect),排除掉一些重复的项,我们把参数数组中的每一项整理归类一下,总共有如下几种类型:
普通函数:
()=>log("a");
延迟函数(Promise):
()=>delay(1000).then(()=>log("d"));
另一个flow:
constsubFlow=createFlow([()=>delay(1000).then(()=>log("c"))]);
用数组包裹的上述三项。
实现
先把参数浅拷贝一份(编写库函数,尽量不要影响用户传入的参数是个原则),再简单的扁平化flat一下。(处理情况4)
functioncreateFlow(effects=[]){ letsources=effects.slice().flat(); }
观察题意,createFlow并不会让方法开始执行,需要.run()之后才会开始执行,所以先定义好这个函数:
functioncreateFlow(effects=[]){ letsources=effects.slice().flat(); functionrun(callback){ while(sources.length){ consttask=sources.shift(); } callback?.(); } }
这里我选择用while循环依次处理数组中的每个effect,便于随时中断。
对于函数类型的effect,直接执行它:
functioncreateFlow(effects=[]){ letsources=effects.slice().flat(); functionrun(callback){ while(sources.length){ consttask=sources.shift(); if(typeoftask==="function"){ constres=task(); } } //在所有任务执行完毕后执行传入的回调函数 callback?.(); } return{ run, isFlow:true, }; }
这里拿到了函数的返回值res,有一个情况别忘了,就是effect返回的是一个Promise,比如这种情况:
()=>delay(1000).then(()=>log("d"));
那么拿到返回值后,这里直接简化判断,看返回值是否有then属性来判断它是否是一个Promise(生产环境请选择更加严谨的方法)。
if(res?.then){ res.then(createFlow(sources).run); return; }
这里我选择中断本次的flow执行,并且用剩下的sources去建立一个新的flow,并且在上一个Promise的then方法里再去异步的开启新的flow的run。
这样,上面延迟1s后的Promise被resolve之后,剩下的sources任务数组会被新的flow.run()驱动,继续执行。
接下来再处理effect是另一个flow的情况,注意上面编写的大致函数体,我们已经让createFlow这个函数返回值带上isFlow
这个标记,用来判断它是否是一个flow。
//把callback放到下一个flow的callback时机里执行 constnext=()=>createFlow(sources).run(callback) if(typeoftask==="function"){ constres=task(); if(res?.then){ res.then(next); return; } }elseif(task?.isFlow){ task.run(next); return; }
看elseif的部分,直接调用传入的flow的run,把剩下的sources创建的新的flow,并且把这一轮的callback放入到新的flow的callback位置。在所有的任务都结束后再执行。
定义一个next方法,用来在遇到异步任务或者另一个flow的时候
这样,参数中传入的flow执行完毕后,才会继续执行剩下的任务,并且在最后执行callback。
完整代码
functioncreateFlow(effects=[]){ letsources=effects.slice().flat(); functionrun(callback){ while(sources.length){ consttask=sources.shift(); //把callback放到下一个flow的callback时机里执行 constnext=()=>createFlow(sources).run(callback) if(typeoftask==="function"){ constres=task(); if(res?.then){ res.then(next); return; } }elseif(task?.isFlow){ task.run(next); return; } } callback?.(); } return{ run, isFlow:true, }; } constdelay=()=>newPromise((resolve)=>setTimeout(resolve,1000)); createFlow([ ()=>console.log("a"), ()=>console.log("b"), createFlow([()=>console.log("c")]), [()=>delay().then(()=>console.log("d")),()=>console.log("e")], ]).run();
总结
这道面试题主要的目的是考察对于异步串行流的控制,巧妙的利用自身的递归设计来处理传入的参数也是一个flow的情况,在编写题目的过程中展示你对Promise的熟练运用,一定会让面试官对你刮目相看的~
到此这篇关于探究一道价值25k的蚂蚁金服异步串行面试题的文章就介绍到这了,更多相关异步串行面试题内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。