您的位置 首页 > 德语词汇

promise是什么意思?用法、例句?你真的懂Promise吗

各位老铁们,大家好,今天由我来为大家分享promise是什么意思?用法、例句,以及你真的懂Promise吗的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!

在异步编程中,Promise扮演了举足轻重的角色,比传统的解决方案(回调函数和事件)更合理和更强大。可能有些小伙伴会有这样的疑问:2020年了,怎么还在谈论Promise?事实上,有些朋友对于这个几乎每天都在打交道的“老朋友”,貌似全懂,但稍加深入就可能疑问百出,本文带大家深入理解这个熟悉的陌生人——Promise.

newPromise(function(resolve,reject){...}/*executor*/)构建Promise对象时,需要传入一个executor函数,主要业务流程都在executor函数中执行。Promise构造函数执行时立即调用executor函数,resolve和reject两个函数作为参数传递给executor,resolve和reject函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。一旦状态改变,就不会再变,任何时候都可以得到这个结果。在executor函数中调用resolve函数后,会触发promise.then设置的回调函数;而调用reject函数后,会触发promise.catch设置的回调函数。

值得注意的是,Promise是用来管理异步编程的,它本身不是异步的,newPromise的时候会立即把executor函数执行,只不过我们一般会在executor函数中处理一个异步操作。比如下面代码中,一开始是会先打印出2。

promise是什么意思?用法、例句?你真的懂Promise吗

letp1=newPromise(()=>{\nsetTimeout(()=>{\nconsole.log(1)\n},1000)\nconsole.log(2)\n})\nconsole.log(3)//231

Promise采用了回调函数延迟绑定技术,在执行resolve函数的时候,回调函数还没有绑定,那么只能推迟回调函数的执行。这具体是啥意思呢?我们先来看下面的例子:

letp1=newPromise((resolve,reject)=>{\nconsole.log(1);\nresolve('浪里行舟')\nconsole.log(2)\n})\n//then:设置成功或者失败后处理的方法\np1.then(result=>{\n//p1延迟绑定回调函数\nconsole.log('成功'+result)\n},reason=>{\nconsole.log('失败'+reason)\n})\nconsole.log(3)\n//1\n//2\n//3\n//成功浪里行舟

newPromise的时候先执行executor函数,打印出1、2,Promise在执行resolve时,触发微任务,还是继续往下执行同步任务,执行p1.then时,存储起来两个函数(此时这两个函数还没有执行),然后打印出3,此时同步任务执行完成,最后执行刚刚那个微任务,从而执行.then中成功的方法。

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被onReject函数处理或catch语句捕获为止。具备了这样“冒泡”的特性后,就不需要在每个Promise对象中单独捕获异常了。

要遇到一个then,要执行成功或者失败的方法,但如果此方法并没有在当前then中被定义,则顺延到下一个对应的函数

functionexecutor(resolve,reject){\nletrand=Math.random()\nconsole.log(1)\nconsole.log(rand)\nif(rand>0.5){\nresolve()\n}else{\nreject()\n}\n}\nvarp0=newPromise(executor)\nvarp1=p0.then((value)=>{\nconsole.log('succeed-1')\nreturnnewPromise(executor)\n})\nvarp2=p1.then((value)=>{\nconsole.log('succeed-2')\nreturnnewPromise(executor)\n})\np2.catch((error)=>{\nconsole.log('error',error)\n})\nconsole.log(2)

这段代码有三个Promise对象:p0~p2。无论哪个对象里面抛出异常,都可以通过最后一个对象p2.catch来捕获异常,通过这种方式可以将所有Promise对象的错误合并到一个函数来处理,这样就解决了每个任务都需要单独处理异常的问题。

通过这种方式,我们就消灭了嵌套调用和频繁的错误处理,这样使得我们写出来的代码更加优雅,更加符合人的线性思维。

我们都知道可以把多个Promise连接到一起来表示一系列异步骤。这种方式可以实现的关键在于以下两个Promise固有行为特性:

先通过下面的例子,来解释一下刚刚这段话是什么意思,然后详细介绍下链式调用的执行流程

letp1=newPromise((resolve,reject)=>{\nresolve(100)//决定了下个then中成功方法会被执行\n})\n//连接p1\nletp2=p1.then(result=>{\nconsole.log('成功1'+result)\nreturnPromise.reject(1)\n//返回一个新的Promise实例,决定了当前实例是失败的,所以决定下一个then中失败方法会被执行\n},reason=>{\nconsole.log('失败1'+reason)\nreturn200\n})\n//连接p2\nletp3=p2.then(result=>{\nconsole.log('成功2'+result)\n},reason=>{\nconsole.log('失败2'+reason)\n})\n//成功1100\n//失败21

我们通过返回Promise.reject(1),完成了第一个调用then创建并返回的promisep2。p2的then调用在运行时会从returnPromise.reject(1)语句接受完成值。当然,p2.then又创建了另一个新的promise,可以用变量p3存储。

newPromise出来的实例,成功或者失败,取决于executor函数执行的时候,执行的是resolve还是reject决定的,或executor函数执行发生异常错误,这两种情况都会把实例状态改为失败的。

p2执行then返回的新实例的状态,决定下一个then中哪一个方法会被执行,有以下几种情况:

newPromise(resolve=>{\nresolve(a)//报错\n//这个executor函数执行发生异常错误,决定下个then失败方法会被执行\n}).then(result=>{\nconsole.log(`成功:${result}`)\nreturnresult*10\n},reason=>{\nconsole.log(`失败:${reason}`)\n//执行这句时候,没有发生异常或者返回一个失败的Promise实例,所以下个then成功方法会被执行\n//这里没有return,最后会返回undefined\n}).then(result=>{\nconsole.log(`成功:${result}`)\n},reason=>{\nconsole.log(`失败:${reason}`)\n})\n//失败:ReferenceError:aisnotdefined\n//成功:undefinedasync&await

从上面一些例子,我们可以看出,虽然使用Promise能很好地解决回调地狱的问题,但是这种方式充满了Promise的then()方法,如果处理流程比较复杂的话,那么整段代码将充斥着then,语义化不明显,代码不能很好地表示执行流程。

ES7中新增的异步编程方法,async/await的实现是基于Promise的,简单而言就是async函数就是返回Promise对象,是generator的语法糖。很多人认为async/await是异步操作的终极解决方案:

不过也存在一些缺点,因为await将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了await会导致性能上的降低。

asyncfunctiontest(){\n//以下代码没有依赖性的话,完全可以使用Promise.all的方式\n//如果有依赖性的话,其实就是解决回调地狱的例子了\nawaitfetch(url1)\nawaitfetch(url2)\nawaitfetch(url3)\n}

观察下面这段代码,你能判断出打印出来的内容是什么吗?

letp1=Promise.resolve(1)\nletp2=newPromise(resolve=>{\nsetTimeout(()=>{\nresolve(2)\n},1000)\n})\nasyncfunctionfn(){\nconsole.log(1)\n//当代码执行到此行(先把此行),构建一个异步的微任务\n//等待promise返回结果,并且await下面的代码也都被列到任务队列中\nletresult1=awaitp2\nconsole.log(3)\nletresult2=awaitp1\nconsole.log(4)\n}\nfn()\nconsole.log(2)\n//1234

如果await右侧表达逻辑是个promise,await会等待这个promise的返回结果,只有返回的状态是resolved情况,才会把结果返回,如果promise是失败状态,则await不会接收其返回结果,await下面的代码也不会在继续执行。

letp1=Promise.reject(100)\nasyncfunctionfn1(){\nletresult=awaitp1\nconsole.log(1)//这行代码不会执行\n}

我们再来看道比较复杂的题目:

console.log(1)\nsetTimeout(()=>{console.log(2)},1000)\nasyncfunctionfn(){\nconsole.log(3)\nsetTimeout(()=>{console.log(4)},20)\nreturnPromise.reject()\n}\nasyncfunctionrun(){\nconsole.log(5)\nawaitfn()\nconsole.log(6)\n}\nrun()\n//需要执行150ms左右\nfor(leti=0;i<90000000;i++){}\nsetTimeout(()=>{\nconsole.log(7)\nnewPromise(resolve=>{\nconsole.log(8)\nresolve()\n}).then(()=>{console.log(9)})\n},0)\nconsole.log(10)\n//1531047892

做这道题之前,读者需明白:

Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。Promise.resolve()等价于下面的写法:

Promise.resolve('foo')\n//等价于\nnewPromise(resolve=>resolve('foo'))

Promise.resolve方法的参数分成四种情况。

如果参数是Promise实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

constp1=newPromise(function(resolve,reject){\nsetTimeout(()=>reject(newError('fail')),3000)\n})\nconstp2=newPromise(function(resolve,reject){\nsetTimeout(()=>resolve(p1),1000)\n})\np2\n.then(result=>console.log(result))\n.catch(error=>console.log(error))\n//Error:fail

上面代码中,p1是一个Promise,3秒之后变为rejected。p2的状态在1秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了2秒,p1变为rejected,导致触发catch方法指定的回调函数。

(2)参数不是具有then方法的对象,或根本就不是对象

Promise.resolve("Success").then(function(value){\n//Promise.resolve方法的参数,会同时传给回调函数。\nconsole.log(value);//"Success"\n},function(value){\n//不会被调用\n});

(3)不带有任何参数

Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的Promise对象。如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve()方法。

Promise.resolve().then(function(){\nconsole.log('two');\n});\nconsole.log('one');\n//onetwo

(4)参数是一个thenable对象

thenable对象指的是具有then方法的对象,Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法。

letthenable={\nthen:function(resolve,reject){\nresolve(42);\n}\n};\nletp1=Promise.resolve(thenable);\np1.then(function(value){\nconsole.log(value);//42\n});2、Promise.reject()

Promise.reject()方法返回一个带有拒绝原因的Promise对象。

newPromise((resolve,reject)=>{\nreject(newError("出错了"));\n});\n//等价于\nPromise.reject(newError("出错了"));\n\n//使用方法\nPromise.reject(newError("BOOM!")).catch(error=>{\nconsole.error(error);\n});

值得注意的是,调用resolve或reject以后,Promise的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。

newPromise((resolve,reject)=>{\nreturnreject(1);\n//后面的语句不会执行\nconsole.log(2);\n})3、Promise.all()

letp1=Promise.resolve(1)\nletp2=newPromise(resolve=>{\nsetTimeout(()=>{\nresolve(2)\n},1000)\n})\nletp3=Promise.resolve(3)\nPromise.all([p3,p2,p1])\n.then(result=>{\n//返回的结果是按照Array中编写实例的顺序来\nconsole.log(result)//[3,2,1]\n})\n.catch(reason=>{\nconsole.log("失败:reason")\n})

Promise.all生成并返回一个新的Promise对象,所以它可以使用Promise实例的所有方法。参数传递promise数组中所有的Promise对象都变为resolve的时候,该方法才会返回,新创建的Promise则会使用这些promise的值。

如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的Promise对象。

有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,ES2020引入Promise.allSettled()方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()方法无法做到这一点。

假如有这样的场景:一个页面有三个区域,分别对应三个独立的接口数据,使用Promise.all来并发请求三个接口,如果其中任意一个接口出现异常,状态是reject,这会导致页面中该三个区域数据全都无法出来,显然这种状况我们是无法接受,Promise.allSettled的出现就可以解决这个痛点:

Promise.allSettled([\nPromise.reject({code:500,msg:'服务异常'}),\nPromise.resolve({code:200,list:[]}),\nPromise.resolve({code:200,list:[]})\n]).then(res=>{\nconsole.log(res)\n/*\n0:{status:"rejected",reason:{…}}\n1:{status:"fulfilled",value:{…}}\n2:{status:"fulfilled",value:{…}}\n*/\n//过滤掉rejected状态,尽可能多的保证页面区域数据渲染\nRenderContent(\nres.filter(el=>{\nreturnel.status!=='rejected'\n})\n)\n})

Promise.allSettled跟Promise.all类似,其参数接受一个Promise的数组,返回一个新的Promise,唯一的不同在于,它不会进行短路,也就是说当Promise全部处理完成后,我们可以拿到每个Promise的状态,而不管是否处理成功。

Promise.all()方法的效果是"谁跑的慢,以谁为准执行回调",那么相对的就有另一个方法"谁跑的快,以谁为准执行回调",这就是Promise.race()方法,这个词本来就是赛跑的意思。race的用法与all一样,接收一个promise对象数组为参数。

Promise.all在接收到的所有的对象promise都变为FulFilled或者Rejected状态之后才会继续进行后面的处理,与之相对的是Promise.race只要有一个promise对象进入FulFilled或者Rejected状态的话,就会继续进行后面的处理。

//`delay`毫秒后执行resolve\nfunctiontimerPromisefy(delay){\nreturnnewPromise(resolve=>{\nsetTimeout(()=>{\nresolve(delay);\n},delay);\n});\n}\n//任何一个promise变为resolve或reject的话程序就停止运行\nPromise.race([\ntimerPromisefy(1),\ntimerPromisefy(32),\ntimerPromisefy(64)\n]).then(function(value){\nconsole.log(value);//=>1\n});

上面的代码创建了3个promise对象,这些promise对象会分别在1ms、32ms和64ms后变为确定状态,即FulFilled,并且在第一个变为确定状态的1ms后,.then注册的回调函数就会被调用。

ES9新增finally()方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在then()和catch()中各写一次的情况。

比如我们发送请求之前会出现一个loading,当我们请求发送完成之后,不管请求有没有出错,我们都希望关掉这个loading。

this.loading=true\nrequest()\n.then((res)=>{\n//dosomething\n})\n.catch(()=>{\n//logerr\n})\n.finally(()=>{\nthis.loading=false\n})

finally方法的回调函数不接受任何参数,这表明,finally方法里面的操作,应该是与状态无关的,不依赖于Promise的执行结果。

假设有这样一个需求:红灯3s亮一次,绿灯1s亮一次,黄灯2s亮一次;如何让三个灯不断交替重复亮灯?三个亮灯函数已经存在:

functionred(){\nconsole.log('red');\n}\nfunctiongreen(){\nconsole.log('green');\n}\nfunctionyellow(){\nconsole.log('yellow');\n}

这道题复杂的地方在于需要“交替重复”亮灯,而不是亮完一遍就结束的一锤子买卖,我们可以通过递归来实现:

//用promise实现\nlettask=(timer,light)=>{\nreturnnewPromise((resolve,reject)=>{\nsetTimeout(()=>{\nif(light==='red'){\nred()\n}\nif(light==='green'){\ngreen()\n}\nif(light==='yellow'){\nyellow()\n}\nresolve()\n},timer);\n})\n}\nletstep=()=>{\ntask(3000,'red')\n.then(()=>task(1000,'green'))\n.then(()=>task(2000,'yellow'))\n.then(step)\n}\nstep()

同样也可以通过async/await的实现:

//async/await实现\nletstep=async()=>{\nawaittask(3000,'red')\nawaittask(1000,'green')\nawaittask(2000,'yellow')\nstep()\n}\nstep()

使用async/await可以实现用同步代码的风格来编写异步代码,毫无疑问,还是async/await的方案更加直观,不过深入理解Promise是掌握async/await的基础。

END,本文到此结束,如果可以帮助到大家,还望关注本站哦!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023