序大家好,我是林三鑫。用最通俗易懂的语言解释最难的知识点是我的座右铭。这很重要。Promise的使用层次我觉得可以分为三个层次。1、掌握Promise的基本使用。2.掌握Promise的基本原理。其实就是能够掌握一些基本的使用方法和Promise的一些方法,比如then,catch,all,race,finally,allSettled,any,resolve等。第二点是能够简单的实现Promise的原理,可以让我们对Promise的常用方法有更深入的了解,第三点就是能够灵活的使用Promise来解决我们开发中的一些问题。今天给大家说说我在项目开发中用Promise解决了哪些问题!接口请求超时,顾名思义,就是给一个时间,如果接口请求超过这个时间,就会报错1.实现思路是:接口请求和延时函数赛跑,使用一个Promise把它包装起来,因为Promise的状态是不可逆的,所以如果接口请求先跑完了,说明还没有超时,Promise的状态是fulfilled。反之,如果delay函数先运行,说明已经超时,Promise的状态为rejetced。最后根据Promise的状态判断是否超时/***Simulationdelay*@param{number}delay延迟时间*@returns{Promise}*/functionsleep(delay){returnnewPromise((_,reject)=>{setTimeout(()=>reject('timeout'),delay)})}/***模拟请求*/functionrequest(){//假设请求需要1s返回newPromise(resolve=>{setTimeout(()=>resolve('成功'),1000)})}/***判断是否超时*@param{()=>Promise}requestFn请求函数*@param{number}delay延迟时间*@returns{Promise}*/functiontimeoutPromise(requestFn,delay){returnnewPromise((resolve,reject)=>{constpromises=[requestFn(),sleep(delay)]for(constpromiseofpromises){//超时则执行失败,未超时则执行成功promise.then(res=>resolve(res),err=>reject(err))}})}2.Promise.race其实timeoutPromise中的代码可以换成Promise.race,效果相同的函数timeoutPromise(requestFn,delay){//如果延迟的Promise先返回,则说明超时returnPromise.race([requestFn(),sleep(delay)])}3.测试//超时timeoutPromise(request,500).catch(err=>console.log(err))//超时//没有超时timeoutPromise(request,2000).then(res=>console.log(res))//转盘抽奖成功当我们通常开始旋转转盘,我们通常开始旋转同时发起接口请求,所以有两种可能。1、转盘后接口没有请求返回,异常。2.接口请求在转盘转动前完成。这是正常的,但是需要保证请求回调跟在转盘的后面转盘转动后的回调是同时执行的。主要问题是如何判断接口请求时间是否超过了转盘完成转动所需的时间。我们其实可以利用前面的知识点。接口请求超时是一样的。原因如果转盘转完需要2500ms,那么我们可以限制接口请求提前1000ms返回,即接口请求的超时时间为2500ms-1000ms=1500ms/***simulationdelay*@param{number}delay延迟时间*@returns{Promise}*/functionsleep(delay){returnnewPromise((_,reject)=>{setTimeout(()=>reject('timeout'),delay)})}/***模拟请求*/functionrequest(){returnnewPromise(resolve=>{setTimeout(()=>resolve('successful'),1000)})}/***判断是否超时*@param{()=>Promise}requestFn请求函数*@param{number}delay延迟时间*@returns{Promise}*/functiontimeoutPromise(requestFn,delay){returnPromise.race([requestFn(),sleep(delay)])}2.转盘未结束前,接口请求完成。我们已经保证了在转盘结束前可以请求回接口请求,但是还有一个问题,就是需要保证请求回调和转盘结束回调是同时执行的,因为虽然接口请求回来的时候转盘还在转,需要等转盘转完了,再把两个回调一起执行。听到这个描述,相信很多同学都会想到Promise.all这个方法//。..上面的代码/***模拟转盘到停止的延迟*@param{number}delay延迟时间*@returns{Promise}*/functionturntableSleep(delay){returnnewPromise(resolve=>{设置超时(()=>resolve('Stopturning'),delay)})}/***判断是否超时*@param{()=>Promise}requestFn请求函数*@param{number}turntableDelay多久转盘转动*@param{number}延迟请求超时长度*@returns{Promise}*/functionzhuanpanPromise(requsetFn,turntableDelay,delay){returnPromise.all([timeoutPromise(requsetFn,delay),turntableSleep(turntableDelay)])}3.测试//没有超时,在转盘停止前请求数据返回zhuanpanPromise(request,2500,1500).then(res=>console.log(res),err=>console.log(err))来控制并发Promise调度器想象一下,有一天你突然一下子发送了10个请求,但是在这种情况下,并发量非常大,你能不能控制一下,即一次只发送2个请求,而当某个请求结束了,让第三个加入,请求结束了,让第四个加入,以此类推,让最高并发变得可控addTask(1000,"1");addTask(500,"2");addTask(300,"3");addTask(400,"4");输出序列为:2314整个完整的执行过程:at一开始,当1和2两个任务开始执行500ms时,任务2执行完毕,输出2。当任务3开始执行800ms时,执行任务3,输出3。当任务4开始执行1000ms时,执行任务1,输出1。此时只有任务4执行了1200ms。任务4被执行,输出为4;{returnnewPromise((resolve,reject)=>{setTimeout(()=>{console.log(order)resolve()},time)})}this.queue.push(promiseCreator)}taskStart(){for(leti=0;i=this.limit)returnthis.count++this.queue.shift().then(()=>{this.count--this.request()})}}test//测试constscheduler=newScheduler(2);constaddTask=(time,order)=>{scheduler.add(time,order);};addTask(1000,"1");addTask(500,"2");addTask(300,"3");addTask(400,"4");scheduler.taskStart();取消重复请求比如我们在提交表单时,为了防止多次重复提交,我们肯定会在按钮的点击事件中加入防抖措施,这确实有效避免了多次点击导致的重复请求,但其实众所周知还是有弊端的。为了更好的用户体验,防抖延时不宜过长。一般在我的项目中是300ms,但是这个只能管理请求时间<300ms的接口请求,如果有接口请求耗时2000ms,那么此时防抖是不能完全限制重复请求的,所以我们需要做额外的处理来取消重复的请求。实现思路:简单的说,使用Promise的.race方法,在每个请求的旁边安装一个地雷,如果在第一个请求之后接收到第二个请求如果重复请求,则执行第一个请求旁边的地雷,炸掉第一个请求,以此类推classCancelablePromise{constructor(){this.pendingPromise=nullthis.reject=null}request(requestFn){if(this.pendingPromise){this.cancel('取消重复请求')}constpromise=newPromise((_,reject)=>(this.reject=reject))this.pendingPromise=Promise.race([requestFn(),promise])returnthis.pendingPromise}cancel(reason){this.reject(reason)this.pendingPromise=null}}functionrequest(delay){return()=>newPromise(resolve=>{setTimeout(()=>{resolve('Thewinnerisme')},delay)})}testconstcancelPromise=newCancelablePromise()//模拟频繁请求5次for(leti=0;i<5;i++){cancelPromise.request(request(2000)).then((res)=>console.log(res))//最后一个last获胜者是me.catch((err)=>console.error(err));//前四个取消重复请求}全局请求加载比如一个页面或者多个组件需要请求并显示加载状态,这个有时候我们不想为每个页面或者组件写一次加载,那么我们可以管理统一加载。loading有两种情况:1.全局,只要request中还有接口,就会显示loading2.全局,所有如果接口不在请求中,则隐藏加载。怎么才能知道全局接口的请求状态呢?其实我们可以使用Promise。只要一个接口请求的Promise状态不是pending,就说明他的请求已经完成。不管请求成功与否,既然成功与失败无关紧要,我们就会想到Promise.prototype.finally这个方法来实现类PromiseManager{constructor(){this.pendingPromise=newSet()this.loading=false}generateKey(){return`${newDate().getTime()}-${parseInt(Math.random()*1000)}`}push(...requestFns){for(constrequestFnofrequestFns){constkey=this.generateKey()this.pendingPromise.add(key)requestFn().finally(()=>{this.pendingPromise.delete(key)this.loading=this.pendingPromise.size!==0})}}}测试//模拟请求函数request(delay){return()=>{returnnewPromise(resolve=>{setTimeout(()=>resolve('Success'),delay)})}}constmanager=newPromiseManager()manager.push(request(1000),request(2000),request(800),request(2000),request(1500))consttimer=setInterval(()=>{//轮询检查加载状态console.log(manager.loading)},300)参考无极技术要点-面试实战版结语如果觉得这篇文章对你有点帮助,点个赞鼓励一下林三鑫哈哈哈或者你也可以加入我的墨鱼群,一起努力学习啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊我会定期模拟面试,复盘指导,答疑解惑,互相学习,共同进步!!