ES6——Promise

  Promise是ES6中新增的一个对象,主要是为了解决异步操作的问题而新增的一个方法

定义

  Promise用来表示一个异步的状态(成功/失败)以及返回的值

  也就是说,Promise里放着的是未来要做的事情

  关于执行的顺序,Promise对象在创建后会立即执行,then在当前所有同步脚本执行完后执行

  例如如下案例

  

  输出的顺序为:Promise、Hello、resolved

  解析如下:

  1.Promise对象创建后立即执行,因此输出了Promise

  2.then对象中的语句要到当前脚本所有同步任务执行完后再执行,所以resolved暂时不输出,先输出Hello

  3.最后,当前脚本内所有的同步任务执行完,执行then里的语句,resolved输出

  下面一个案例也是关于异步同步问题的

  

  输出结果为:123465

  过程如下:

  1.1、4、6不用过多说明,同步任务,按照代码的先后依次执行即可

  2.Promise实例在创建的时候就立刻执行,所以2和3也依次输出

  3.由于then方法是在所有同步任务执行完后执行,所以5最后输出

 

状态

  Promise里共有三种状态:进行中pending)、成功fulfilled)、失败rejected

  其中状态的改变称为决议,而状态只可以从pendding变更到fulfilled/rejected不可逆也不可在fulfilled/rejected里互换

  也就是说在Promise里,只有pending--->fulfilled或者pending--->rejected,没有第三种

 

基本方法

  使用时,需要先构建一个Promise对象的实例

  这个实例接收两个参数,一个是回调成功时执行的函数(习惯性命名resolve),另一个是回调失败时执行的函数(习惯性命名reject)

  而如果想在某次回调后继续链式回调,则返回构建Promise对象实例的函数,这样一来,就是返回了一个Promise实例,继续等待传入回调成功和失败时的执行函数

  当然,还可以向resolve和reject里传入参数,回调的时候可以输出,但只可以传入一个参数,多余的参数不被接收

 

  使用的时候,利用Promise实例中的then方法,接收两个参数,第一个为回调成功时执行的函数(resolve),第二个是回调失败时执行的函数(reject)

  而如果想链式调用,则直接在执行完resolve/reject后返回Promise实例,这样一来,下次继续等待传入两个回调函数

  

  实际上也可以不返回Promise实例,这样一来then里的函数会同之前所有Promise实例后的then一同执行

  例如这里的1和2都是1s后输出的,而345则是12输出后的1s后输出(因为返回的Promise实例是要1s后回调的),可以理解成输出1后返回了一个“啥也没干”的Promise

  then里如果想返回Promise实例,return一定要写在then的执行函数后面

  

 

错误处理

catch

  Promise里可以传入reject函数来对错误进行处理,如果没有,需要用catch语句来接收错误并处理

  

  该段代码的执行顺序如下:

  1.因为给test输入了true,所以执行的是resolve函数,这里resolve函数传入的参数ok,而then里的data只是个形参,执行的最后返回Promise对象并传入false

  2.第二个链式调用的then里,只传入了resolve函数,没有reject函数,但由于这里Promise对象里是false,应该执行reject函数,所以没有输出

  3.第三个链式调用为catch,捕获了第二个then里没有捕获的错误,里面传入的参数可以理解为reject函数e形参,最后输出404

  

  但是,如果错误被reject处理了,那后面的catch就不起作用

  

   实际上,可以在最后的catch里再次返回一个错误,让后面的catch解决,但会出现一个问题,有可能最后一个catch抛出了错误,但是没有解决。这一点上,ES6暂时没有解决方法

finally

  Promise实例中还有个finally方法,可以放在链式调用的最后,其中的代码无论如何都会执行

  注意如果finally执行时还有错误未处理,则会报错,说明Promise里的错误未处理

  

  

 

优点

  Promise提高了代码的可读性可维护性

  Promise的使用最大的好处是,解决了之前多层嵌套回调的问题

  例如,我们需要创建一个回调函数,用来在1s后执行我们传入的一个函数,如果说只是回调一次,直接调用这个回调函数,传入1s后执行的函数,即可

  但如果是要依赖上一次回调的结果,再去执行一次回调函数,那就需要嵌套两层了,不过也还行

  可如果是嵌套到五六层呢?嵌套了这么多层以后,如果需要更改其中两次的执行顺序呢?似乎就没办法操作了,堪称回调地狱(如下图)

  

  可如果使用Promise对象来写这一段代码,就变成了下面的样子

  

  相比于之前的层层套的写法,这里采用了链式调用的方法,如果哪里需要修改内容或顺序,可以直接修改then里的内容,或者调整then的顺序,解决了多层嵌套的问题

  

Promise对象的方法

all

  用来接收一个数组,里面每一项是一个Promise实例,最后包装成一个新的Promise实例

  注意这里传入的必须是数组(可迭代),否则会报错(not iterable)

  分三种情况:

  1.所有的Promise决议为成功,.all的决议也为成功,把所有Promise实例的resolve中传入的参数组成一个数组返回,其顺序符合Promise实例的顺序

  

  2.有一个Promise决议为失败,.all决议为失败,把这个决议失败的reject传入的参数作为错误返回

  有多个Promise决议为失败时,输出的参数只有第一个reject传入的参数

  

  3.传入空数组,决议为成功,输出then中给resolve传入的参数

  

race

  类似于all,race里传入的参数也是Promise实例,不过返回时,按照第一个返回的决议决定race的决议情况,执行then里的resolve或reject

  如果返回的速度一样,则按照传入时的第一个Promise实例的决议来决定执行resolve/reject

  

  但如果传入的为空,则什么都不执行,会一直“挂”在那里

  

resolve&reject

  这里的resolve和reject不是Promise实例或者then里传入的参数,而是Promise实例的方法,用来生成被决议为成功/失败的Promise实例

  也就是说,无论传入的是什么,resolve/reject返回的都是一个Promise实例

resolve

  resolve方法中可以传入:

  1.普通值(字符串、数字等)

  下图中的两种写法实际上是一样的

  

  也就是说,then方法最后返回时,可以直接用Promise.resolve()/reject()返回一个决议成功/失败的Promise实例

  另外,Promise的all方法里,数组中的每一项都会被resolve方法包裹一下,这样每一项都是一个Promise实例

  2.Promise实例

  

  这里传入的Promise实例最后通过resolve方法输出的仍为该实例(最底下一行有验证)

  3.thenable对象

  thenable对象是一个类似于then方法的对象,传入resolve后,会立即执行其中的then方法,按照then方法的套路走

  

  这里的执行结果为“我被执行了”和“哼”

  过程如下:

  1.obj作为一个thenable对象被传入的resolve方法,然后后面的then方法执行时,调用obj里的then方法

  2.这里的回调函数cb就是console.log(data),也就是输出哼,但在这之前先输出“我被执行了”

reject

  而Promise.reject()相比于resolve简单得多,会把传入其中的值直接当成错误信息输出

  

应用

  可以用then变成异步任务并在当前脚本所有同步任务执行后再执行的特性,把同步任务变成异步任务

  

  上方的执行结果为:我是同步任务、我变成了异步任务、2

  具体过程如下:

  1.用createAsyncTask函数,传入一个匿名函数,这个函数由于传入了Promise的resolve方法,变成了一个Promise实例,但是执行是在后面的then里,因此变成了异步任务,稍后执行

  2.“我是同步任务”直接输出

  3.输出所有同步语句后,对then里的语句进行执行,由于这里两句都是同步任务,所以按顺序输出

 

综合案例

  可以利用Promise的各种方法实现当图片全部加载完后再载入

  (案例待补充)

posted @ 2019-09-05 22:30  且听风吟720  阅读(158)  评论(0编辑  收藏  举报