Promise是异步任务同步化的解决方案?本文讲通过以下几方面来介绍Promise:
- Promise的出现为了解决什么问题
- Promise内部对任务的处理
- Promise的标准规范
- Promise的方法使用
- Promise在eventLoop中执行顺序
- 源码地址
Promise知识分五个章节来阐述,这篇我们讨论:Promise的标准规范。
Promise的规范有很多,比如Promise/A,Promise/B,Promise/D,Promise/A升级版,Promise/A+。ES6中采用了Promise/A+规范。
本节可能描述抽象,不涉及到应用层面,但搞懂后会对你使用Promise有很大提升。
其他规范了解
简单的说说Promise/A/B/D的规范吧,他们都是由CommonJS组织提出的,最早提出Promise/A,与现在用的Promise类似,Promise/B时在A基础上定义了一组Promise模块需要实现的API(when(),asap(),enquene()...),Promises/D 规范添加如何判断一个对象是Peomise,对B定义的ref,reject,def,defer方法做了进一步约束。 我们真正要了解的是Promise/A+规范,是Promise/A+组织发布的规范,以Promise/A为基础进行补充修订。
术语
Promise
是一个包含了兼容promise规范then方法的对象或函数。
thenable
是一个包含了then方法的对象或函数。
value
是任意js值。
exception
是由throw表达式抛出来的值。
reason
是一个用于描述Promise被拒绝原因的值。
Promise规范要求
- Promise有三种状态:
pending
、fulfilled
、rejected
。 - Promise含有then方法。
- Promise含有Promise Resolution Procedure(Promise的状态解析方法)。
- 其他Promise中挂载的方法返回值
Promise状态
Promise必须处于pending
,fulfilled
,rejected
三种状态其中一种。
- 我们实例化一个Promise时,传入一个函数作为参数,此参数称之为executor(执行函数),他可以告诉我们何时将Promise状态从
pending
转换成其余两种形态中的一种。\ - then()是被实例化出的一个方法,他能传入两个函数作为参数,作为
fulfilled
,rejected
两个状态,当Promise从pending
转化成其他状态时会触发对应回调,并且它不能再转换成其他状态。
let p = new Promise((resolve, reject)=>{ //为executor函数
let x = Math.random();
if(x > 0.5) resolve('由pending变为fulfilled');
reject('由pending变为rejected')
})
p.then(value =>{},reason=>{}) //绑定成功后的回调函数
.then方法
再详细说说.then方法:
1.它接收两个参数。
promise.then(onFulfilled, onRejected)
onFulfilled
,onRejected
都是可选参数,一般为函数。如果onFulfilled
,onRejected
不是一个函数,必须被忽略。
当onFulfilled
为函数时:
- 它必须是Promise被解决后才调用,Promise中
resolve
传的值作为他第一个参数 - 它一定不能被调用多次
当
onRejected
为函数时: - 它必须是Promise被拒绝后才调用,Promise中
reject
传的值作为他第一个参数 - 它一定不能被调用多次
2.同一个Promise上的then可能被调用多次:
如果promise被解决,所有相应的onFulfilled回调必须按照他们原始调用then的顺序执行。
如果promise被拒绝,所有相应的onRejectd回调必须按照他们原始调用then的顺序执行。
举例好说明
//第一题
Promise.resolve().then(()=>{
console.log(1) //1 resolved
}).then(val=>{
console.log(2) // 2 resolved
},err=>{
console.log(3)
})
//返回resolved状态 log结果:1,2
//第二题
Promise.resolve().then(val=>{
console.log(1) //1
throw new Error("error1")
}).then(val=>{
console.log(2)
},err=>{
console.log(3)
}).then(val=>{
console.log(4)
throw new Error("error2")
},err=>{
console.log(5)
}).then(val =>{
console.log(6)
},err=>{
console.log(7)
})
//返回resolved状态 log结果:1,3,4,7
从例子我们可以看出,无论是解决和拒绝,都要继续执行.then方法,下次的.then根据上次.then执行的结果,执行不用的回调。同时这也体现出.then方法的另一个特性:
3.then方法必会返回一个新的Promise。
注意:在多次调用.then方法,会出现值的穿透,第一个then的参数会穿透到第二个then方法。
let p = new Promise((resolve,reject)=>{
resolve('resolve');
});
p.then().then((value)=>{
console.log(value); //会输出resolve
});
Promise Resolution Procedure(Promise的状态解析方法)
Promise状态解析方法的作用是将then时返回的promise2的状态改变并赋予其vlaue/reason。
在网上找了很多资料,都是文字描述,很晦涩难懂,下面我用文字加代码的形式,带大家了解什么是状态解析:
下面参数说的是 Promise.resolve(x) 中的x。
一共四种情况:
1.如果参数是Promise实例本身,则抛出错误。
let x = Promise.resolve(x)
x.then(
(val) => { console.log('onFilfelled', val); },
(err) => { console.log('onRejected', err); }
)
//Uncaught ReferenceError: Cannot access 'x' before initialization
2.如果参数是一个Promise对象,则then函数的执行取决于这个参数的状态,如果参数也调用了resolve(y),其中y也是一个promise对象then函数的执行取决于这个promise对象,以此类推。
let p = new Promise((resolve, reject) => {
reject('fail')
})
let x = Promise.resolve(p)
x.then(
(val) => { console.log('onFilfelled', val); },
(err) => { console.log('onRejected', err); }
)
//onRejected fail
3.如果参数是一个thenable对象,就是一个对象包含then这个属性,或者是一个函数包含一个then的静态方法,那么直接执行then函数。
let a = {
then: () => { console.log(123); }
}
let x = Promise.resolve(a)
console.log(321);
// 321 123
这里先存疑,为什么会先执行321 再执行123 了解当带.then方法的变量传递给 Promise.resolve() 会自动执行.then方法。
4.如果参数是一个普通值,直接变成onFullfilled状态,然后执行后面的then函数。
let x = Promise.resolve(10)
x.then(
(val) => { console.log('onFilfelled', val); },
(err) => { console.log('onRejected', err); }
)
//onFilfelled 10
这里就是状态解析方法对状态判断的机制,接下来说一下为什么先打印321,.then()是个异步任务,会在下一个tick再执行,但如果是一下这种情况
new Promise((resolve, reject) => {
let a = new Promise(resolve => {
resolve(10)
})
let b = new Promise(resolve => {
let c = Promise.resolve(10)
resolve(c)
})
console.log(a)
console.log(b)
Promise.resolve(0)
.then(() => {
console.log('micro1')
console.log(b)
})
.then(() => {
console.log('micro2')
console.log(b)
})
})
// setTimeout(() => {
// console.log('marco')
// console.log(b)
// })
//结果
Promise {<resolved>: 10}
Promise {<pending>}
4
micro1
Promise {<resolved>: 10}
Promise {<pending>}
micro2
Promise {<resolved>: 10}
我们发现,a直接输出成功状态10,而b在第一个then中输出结果为b中定义的Promise c,所以为执行状态为pending
。但第二个then中b直接输出resolved
状态的10了,这里我们把状态的改变过程称之为状态同步。
对eventLoop熟悉的朋友可能会知道,当第一个tick执行完毕,进入第二个tick时,Promise的状态同步会放在官方称呼PromiseResolveThenableJob中执行,而PromiseResolveThenableJob会放在PromiseJobs的job queue执行,而job queue是microTask的别名,也就是说第二次tick执行的微任务其实会把b的pending状态改为resolved状态。大家也可以复制下代码,把.then的回调删除,直接使用宏任务setTimeout
进入下一个循环中对比。也可看到第一次状态为pending
,在setTimeout
中的为resolved
。
如果此文章对您有帮助或启发,那便是我的荣幸