前端必备:从头开始,搞懂Promise

未来值

设想场景:你去汉堡店买汉堡,购买人数很多,无法立刻拿到汉堡。

当你付款完成后,就发出了对某个值(这里就是汉堡)的请求。此时店员就会给你一个东西代替汉堡,通常是带有编号的收据,那么收据上的编号就是你核销汉堡的凭证,这个编号也代表了汉堡店给你的承诺(promise),即,未来会给你一个汉堡。

此时有了收据,就可以等待(等待态)了,在等待的途中,你可以做其他的事情。

但是汉堡依旧在你的大脑中,你不会等到汉堡送来时产生“为什么给我个汉堡?”等其它疑惑,所以当汉堡送来时,你就会直接处理这个值(汉堡)——把它吃掉。

此时这个在大脑中的汉堡,你也可以理解为即将得到的汉堡,是一个“未来值”,即占住了位置,但并没有拿到实物进行处理,反而你正在做其他事。

等待片刻后,叫到了你的号码,你可以去领汉堡了。

此时会发生3种情况。

  1. 核对编号成功,完成交易,拿到汉堡。即,我需要的值(汉堡),已经处理好了,我拿承诺值(商家给的编号)换取这个值本身(汉堡)。此时,未来值成功了(得到汉堡了),结果就是你就开始吃汉堡了,吃饱了。

  2. 核对编号成功,汉堡卖完了。值(汉堡)没了,未来值(即将得到汉堡)失败了(汉堡并没得到),结果是你可能会换一个地方吃或者干别的。

  3. 商家系统故障,你的号永远都没被叫到。现实中有现实中的处理办法,但在程序中,这是一种未决议状态

从上面的例子可以看出,核对编号到拿到/未拿到汉堡到最终是吃还是没吃的结果,这是一个完整的过程,重要的是最后的结果,是吃了还是没吃。

这基本诠释了使用Promise的整个流程,即Promise先决议,然后确定未来值的状态,有成功也有失败,成功后对应一种结果返回,失败后对应一种结果返回,且只会有一种结果被返回

未决议状态会放在之后讨论。

Promise

Promise:从语义上可以看出,它是一个承诺,承诺一段时间后给你反馈一个结果,从语法上,它是一个对象,使用new来创建,它会有三种状态,等待pending、成功resolve、失败rejecte,且一旦状态确定,就无法更改,此时它成了不变值

Promise创建的任务是微任务。

未决议状态会放在之后讨论,它不是主观触发的一种状态,而以上三种,是promise必备的状态。

此时你是否想到未来值,它也有成功/失败的状态。

所以说:Promise会很好的展现未来值的特性,是一种封装和组合未来值的易于复用的机制。

来看看Promise的使用语法。

let p1 = new Promise((resolve, reject) => {
      // resolve("成功");
         reject("拒绝");
        })

使用决议函数reject()表示失败(请求拒绝)。

使用决议函数resolve()表示成功。

注意,这里为了一次展示语法也为了代码能正常执行,我将resolve(“成功”)注释掉了,因为状态一旦确定就无法更改,如果没有注释,那意思就是,先成功,后又改为失败,这样是不行的,只能有一个状态存在,所以将其中一个注释掉。

使用new来创建的promsie是标准promise。

Promise内封装了“等待底层值的完成或拒绝”的状态,这个状态依赖于时间,所以是这个状态与时间有关,而promise本身与时间无关,可以得出:Promise可以按照可预测的方式进行组合,不用担心时序带来的变化或底层的结果。这可以避免很多传统异步的问题,之后我们会详细讨论。

同样的,Promise的状态不可更改会带来一些好处:

使用Promise决议过的值是不变值,外部无法进行更改,那么,将这个值传给第三方,或者多方进行观察,它就更加安全了,因为无法被修改。

由于状态不可更改,导致Promise产生的任务有以下特点:单一、不可逆、不可撤销,所以,一个Promise,不能有两种状态赋予。

单独的Promise展现了未来值的特性,但是,就像核销编号完成后拿到/未拿到汉堡所产生的结果例如:吃汉堡或换家店一样,Promise完成之后,会有一个东西来执行后面的事情,然后返回一些东西。

使用then()方法来执行得到成功/失败状态后的事情。

Then()

then()方法就像是promise的配套组件,每一个promise都会有一个对应的then()

then()方法内有两个参数,即两个方法,分别接收resolve()和reject()传递过来的“信号”,收到成功状态的信号,就传给then()的第一个函数value,收到失败状态的信号,就传给第二个参数reason,这里使用箭头函数,你可以使用其它函数方法和命名。

    new Promise((resolve, reject) => {
            resolve("操作成功,这是业务1");
// reject("拒绝状态,这是拒绝的业务处理 ");

    })
      .then(
        value => {
          console.log("成功业务处理 1");
        },
        reason => {
          console.log("拒绝的业务处理");
        }
      )

 这里注释掉了reject(),保留了resolve状态,所以传递到then()时就会执行value,输出“成功业务处理1”。

注意:promise对象有几个参数,就要给then()几个参数,即使是null。

一般是两个参数。

new Promise((resolve, reject) => {
            resolve("操作成功,这是业务1");
// reject("拒绝状态,这是拒绝的业务处理 ");

    })
.then(
      msg => {
        console.log("success:" + msg);
      },null)

这里对于reject状态并不想处理,所以没有处理方法,使用null,但不能没有,会报错。

没有null:

前端必备:从头开始,搞懂Promise

Promise不仅可以用于异步问题,还能用于很多地方,这些稍后会说,现在先考虑promise最基本的方法。

链式Promise

在正式开始说链式Promise之前,我们需要搞清楚三个概念。

  1. 每个then()都是一个Promise对象。

  2. 是否在promise中return。

  3. 每个then()会优先处理离自己最近的那个promise。

先来看看第一点。

let p1 = new Promise((resolve, reject) => {
      reject("rejected");
    });
    let p2 = p1
      .then(
        value => console.log(value),
        reason => console.log(reason)
      )
      console.log(p2)

其中p2就是p1的then(),将它打印出来。

前端必备:从头开始,搞懂Promise

可以看到,它是一个准备态的promise。

对它的操作,要么就把它当作一个准备好的微任务并放入任务列表中,要么就将它看作一个promise,为它再配套一个then(),来对这个then()产生的结果再次进行处理。

let p1 = new Promise((resolve, reject) => {
      reject("rejected");
    });
    let p2 = p1
      .then(
        value => console.log(value),
        reason => console.log(reason)
      )
      .then(//.then对上一个promise进行处理,即p1.then
        a => console.log("成功"),
        b => console.log(b)
      );

第二个then()就在处理第一个then()了,可以看下运行结果。

let p1 = new Promise((resolve, reject) => {
      //resolve("fulfilled");
      reject("rejected");
    });
    let p2 = p1
      .then(
        value => console.log(value),
        reason => console.log(reason)
      )
      .then(//.then对上一个promise进行处理,即p1.then
        a => console.log("成功"),
        b => console.log("失败")
      );

注意:两个then()之间不能插入任何的东西。

你觉得输出的结果会是什么?

先看下浏览器的结果,与你的理解可能会有偏差。

前端必备:从头开始,搞懂Promise

 

你会不会觉得第二个输出,应该是“失败”?为什么会是成功?不是reject从上到下传过来的吗?

注意:第二个then()是对上一个then()进行了处理,我们对现在的p2,也就是第一个then()进行输出,看下它的状态。

注意:由于宏任务,微任务,同步任务的关系,我们设置一个定时器来让输出语句在微任务之后进行,才能看到微任务执行后的结果。

let p1 = new Promise((resolve, reject) => {
      //resolve("fulfilled");
      reject("rejected");
    });
    let p2 = p1
      .then(
        value => console.log(value),
        reason => console.log(reason)
      )
      .then(//.then对上一个promise进行处理,即p1.then
        a => console.log("成功"),
        b => console.log(b)
      );

    setTimeout(() => {
        console.log(p2);
    });

前端必备:从头开始,搞懂Promise

可以看到p2的状态是“fulfilled”,也就是成功了,所以在第二个then()中,执行了a,输出“成功”。

原因:第二个then()处理的是第一个then(),第一个then()接收到起始promise的信号,并处理了它,它的处理过程是很成功的,所以它是成功的,它的状态与起始promise无关,所以在接下来的第二个then()处理它时,是按照成功的状态走的,所以执行成功。

那有没有什么办法,让我们自定义传给第二个then()的promise的状态。

当然可以。但是并不是简单的使用then()一直往下串,我们还是需要创建新的promise对象来重新赋予任务状态。

此时就要讨论第二点了,先看上个问题的解决办法。

只需要在第一个then()当中新创建一个promise并且return它即可。

let p1 = new Promise((resolve, reject) => {
      resolve("fulfilled");
    }) 
      .then(
        value => {
          return new Promise((resolve, reject) => {//只要return了,后面的then针对的就是这个return的新promise           
              reject("处理失败");
          })
        },
        reason => console.log(reason)
      )
      .then(//此时的then是对上面的then的处理
        value => {
          console.log("成功:" + value);
        },
        reason => console.log("error" + reason)
      );

在浏览器中查看结果。

前端必备:从头开始,搞懂Promise

起初的promise状态为resolve,所以执行value中的函数,value将新的promise返回,而这个新的promise的状态是reject,所以走到第二个then()中的reason(),打印出“erro处理失败”。

这个过程很像工厂的流水线,比如做面包,原料经过第一个机器,被做成面包胚,再经过第二个机器,被烤熟,最后的结果就是得到一个熟的面包,之前的原料和生面包胚都不复存在,没有人会在意它们,只会在意到手的面包。

但是,注意:第一个机器需要将面包胚吐出来,才能到下一个机器那里进行加工,这就是为什么要return这个promise的原因,我们需要将当前的promise进行返回,才能让下一个then()处理它。

有返回就有不返回,不返回会怎么样呢?

就像原料卡在机器里不出来一样,整个成产线都崩溃了,看看浏览器里的情况。

前端必备:从头开始,搞懂Promise

成功了是因为我们新建的promise并不影响整体流程,像之前说过的一样,经过一轮一轮then()的处理就行,那undefined呢?

Undefined和下一行错误息息相关,因为我们新建了promise却没有给它相对应的then(),所以这个promise是不能正常创建任务的,它什么都没有给下一个then(),但是它的状态是resolve(),所以是“成功:undefined”。

所以说,当你需要一步一步处理你的promise时,千万不要忘了return。

可是,并不总是需要处理标准的promise对象,就像一样的原料,我可以让它变成面包胚、披萨饼、或者其它的东西再传送出去一样,我们也可以返回其它的类型。

例如普通的对象和类。

先来看返回普通的对象。

let p1 = new Promise((resolve, reject) => {
      resolve("fulfilled");
    })
      .then(
        value => {
          return{//返回普通的对象
            name:"Cherrie"
          }
        },
        reason => {}
      )
      .then(
        value => {
          console.log(value);
           console.dir(value);
        },
        reason => {
          console.log(reason);
        }
      );

在浏览器里打印它。 

前端必备:从头开始,搞懂Promise

对象就被返回出来了,你也可以使用它返回其他需要的类型,返回类,修改以上代码即可,这里不多赘述。

在这里强调它可以返回其它类型值的原因是,我们可以通过这个特点来返回除标准promise以外的其它类promise类型,也就是类似标准promise的promise类型,不是我们使用的class哦。

 

 

剩下的内容会尽快更新,每日更新或每两日更新,敬请期待~~

 omise((resolvereject=> {

 

 

 

 

 

 

 

本文由 @龙岩 发布于 职涯宝 ,未经作者许可,禁止转载,欢迎您分享文章

发表评论

登录后才能评论
小程序
小程序
微信客服
微信客服
QQ客服 建站服务
分享本页
返回顶部