未来值
设想场景:你去汉堡店买汉堡,购买人数很多,无法立刻拿到汉堡。
当你付款完成后,就发出了对某个值(这里就是汉堡)的请求。此时店员就会给你一个东西代替汉堡,通常是带有编号的收据,那么收据上的编号就是你核销汉堡的凭证,这个编号也代表了汉堡店给你的承诺(promise),即,未来会给你一个汉堡。
此时有了收据,就可以等待(等待态)了,在等待的途中,你可以做其他的事情。
但是汉堡依旧在你的大脑中,你不会等到汉堡送来时产生“为什么给我个汉堡?”等其它疑惑,所以当汉堡送来时,你就会直接处理这个值(汉堡)——把它吃掉。
此时这个在大脑中的汉堡,你也可以理解为即将得到的汉堡,是一个“未来值”,即占住了位置,但并没有拿到实物进行处理,反而你正在做其他事。
等待片刻后,叫到了你的号码,你可以去领汉堡了。
此时会发生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之前,我们需要搞清楚三个概念。
-
每个then()都是一个Promise对象。
-
是否在promise中return。
-
每个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,为它再配套一个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()之间不能插入任何的东西。
你觉得输出的结果会是什么?
先看下浏览器的结果,与你的理解可能会有偏差。
你会不会觉得第二个输出,应该是“失败”?为什么会是成功?不是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);
});
可以看到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状态为resolve,所以执行value中的函数,value将新的promise返回,而这个新的promise的状态是reject,所以走到第二个then()中的reason(),打印出“erro处理失败”。
这个过程很像工厂的流水线,比如做面包,原料经过第一个机器,被做成面包胚,再经过第二个机器,被烤熟,最后的结果就是得到一个熟的面包,之前的原料和生面包胚都不复存在,没有人会在意它们,只会在意到手的面包。
但是,注意:第一个机器需要将面包胚吐出来,才能到下一个机器那里进行加工,这就是为什么要return这个promise的原因,我们需要将当前的promise进行返回,才能让下一个then()处理它。
有返回就有不返回,不返回会怎么样呢?
就像原料卡在机器里不出来一样,整个成产线都崩溃了,看看浏览器里的情况。
成功了是因为我们新建的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类型,不是我们使用的class哦。
剩下的内容会尽快更新,每日更新或每两日更新,敬请期待~~
omise((resolve, reject) => {