当前位置: 首页 > Web前端 > HTML

关于Promise面试题的思考

时间:2023-03-28 15:17:53 HTML

最近想起在V2EX上看到的一道题:一个异步函数数组,如何一个一个执行?我想做的是每秒打印:这是0这是1这是2~这是8。下面代码的结果是一秒后全部执行。有什么不对的吗,谢谢指教varjobs=[];for(leti=0;i<8;i++){jobs.push(asyncfunction(){setTimeout(function(){console.log("这是"+i)},1000)});}(asyncfunction(){for(constjobofjobs){awaitjob()}})();本题考查对Promise和async/await的理解。而这道题可以让人联想到一个类似setTimeout循环的问题——破解前端面试(80%的应聘者系列失败):从闭包开始,下面的代码打印什么数据for(vari=0;i<5;i++){setTimeout(function(){console.log(newDate,i);},1000);}console.log(newDate,i);0、1、2、3、4、5的顺序排列,要求原代码块中的循环和两个console.log保持不变。如何修改代码?这道题面试官想考察的知识点有循环、闭包、闭包解决方案(IIFE)、ES6知识点(let、Promise)、ES7的Async/await、setTimeout的第三个参数。了解一二下面我们说第一个话题。如果我们要达到题主所说的效果,还缺什么呢?可知let形成了一个块级作用域,也就是说每一个周期,jobs都会push一个async函数,这些函数都是同步执行的,但是注意async中的函数也是同步执行的,只有当等待。因此,当i=0时,作业插入一个setTimeout(function(){console.log("thisis0")})当i=1时,作业插入一个setTimeout(function(){console.log("thisis1")})当i=2时,作业插入一个setTimeout(function(){console.log("thisis2")})当i=3时,作业插入一个setTimeout(function(){console.log("thisis3")})...i=7,jobs插入一个setTimeout(function(){console.log("thisis7")})继续执行(asyncfunction(){for(constjobofjobs){等待工作()}})();这里题主理解await需要和async结合使用,于是写了立即执行匿名函数,执行数组jobs,但问题是jobs中的每个子项都执行了asyncfunction(){setTimeout},async在这里有意义吗?jobs.push(asyncfunction(){setTimeout(function(){console.log("thisis"+i)},1000)});如果要await暂停进程和恢复进程(即awaitjob()),我们需要什么?去掉async,让它成为一个普通的函数,结果是一致的。jobs.push(function(){setTimeout(function(){console.log("这是"+i)},1000)});同理,将一个普通函数改成箭头函数也是如此。一秒后,打印出来的还是0~7。根据网友总结:对于promise对象,await会阻塞函数的执行,等待promise的resolve返回值,作为await的结果,然后执行下一个表达式对于非promise对象,比如箭头函数、同步表达式等,await等待函数或直接值的返回,而不是等待它的执行结果。所以如果要await每1秒执行一个job,就需要返回一个promise实例,根据这个逻辑进行transform...jobs.push(function(){returnnewPromise((resolve,reject)=>{setTimeout(function(){resolve()console.log("thisis"+i)},1000)})});...这样,这个问题就解决了。我们的逻辑是在循环中每次往jobs里面插入一个函数,这个函数返回一个实例Promise(也就是await遇到之后会暂停等待异步结束继续后面的代码)在执行awaitjob()的时候,我们知道它是一个循环作业,await让它等待执行。第一个执行完后,第二个依次执行。每人等待1秒,满足题目要求。这里我们知道await是在等待一个promise实例(如果不是promise实例就不用等待),既然说的是Promise,那我们就扩展一下,thenPromise的then方法的链式调用支持链式调用,它的情况是什么?不返回(return)值,值继续上次解析的return返回非Promise实例returnPromise实例不返回constpromise=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('johan')},2000)})promise.then((data)=>{console.log(data)//什么都不返回}).then((data)=>{console.log('secondtime',data)})answer:johan,第二个undefinedreturn非Promise实例constpromise=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('johan')},2000)})promise.then((data)=>{console.log(data)return'Emperorjohan'}).then((data)=>{console.log('secondtime',data)})答案:johan,约翰二世皇帝返回Promise实例constpromise=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('johan')},2000)})promise.then((data)=>{console.log(data)returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(`${data}next`)},4000)})}).then((data)=>{console.log('thesecondtime',data)})Answer:johan,第二次johannext从上面三个例子我们可以知道then方法中的onfulfilled函数和onrejected函数不仅支持不返回,也支持不退货。Promise实例的普通值,并支持一个Promise实例而返回的Promise实例或者非Promise实例的正常值会传递给下一个then方法的onfulfilled函数或者onrejected函数,因为我们知道它是Generator函数的语法糖,async函数返回一个Promise目的。当函数在执行过程中,一旦遇到await,会先返回,等待异步操作完成,再执行函数体后面的语句。拿一道题来测试一下constmyPromise=val=>Promise.resolve(val);constdelay=duration=>{/**/};myPromise(`hello`).then(delay(1000)).then(val=>console.log(val));//hellomyPromise是一个Promise实例,传入值hello,将Promise.resolve传给then,再传给下一个then通过delay打印val,所以delay(1000)会返回一个Promise实例,这样第二个然后可以打印helloconstdelay=duration=>(val)=>newPromise((resolve,reject)=>{setTimeout(()=>{resolve(val)},duration)})