Promise_0">Promise
1)状态不可逆性:
javascript">let p1 = new Promise((resolve, reject) => {resolve('success')reject('fail')
})
console.log('p1', p1)let p2 = new Promise((resolve, reject) => {reject('success')resolve('fail')
})
console.log('p2', p2)let p3 = new Promise((resolve, reject) => {throw('error')
})
console.log('p3', p3)
这里说明了Promise的四个特点:
- 执行了resolve,Promise状态会变成fulfilled;
- 执行了reject,Promise状态会变成rejected;
- Promise状态不可逆,第一次成功就永久为fulfilled,第一次失败就永远状态为rejected;
- Promise中有throw的话,就相当于执行了reject;
Promise_28">手动实现 Promise
- Promise的初始状态是pending;
- 需要对resolve和reject绑定this:确保resolve和reject的this指向永远指向当前的MyPromise实例,防止随着函数执行环境的改变而改变;
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向 this.initBind()// 执行传进来的函数executor(this.resolve, this.reject)}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}
}
测试结果
javascript">const test1 = new MyPromise((resolve, reject) => {resolve('success')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }const test2 = new MyPromise((resolve, reject) => {reject('fail')
})
console.log(test2) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
状态不可变
javascript">const test1 = new MyPromise((resolve, reject) => {resolve('success')reject('fail')
})
console.log(test1) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
正确的应该是状态为fulfilled,但这里状态又变成了rejected。
Promise有三种状态:
- pending:等待中,是初始状态;
- fulfilled:成功状态;
- rejected:失败状态;
一旦状态从pending变为fulfilled或者rejected,那么此Promise实例的状态就不可以改变了。
然后我们来改进一下:
javascript"> resolve(value) {// state是不可变的
+ if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的
+ if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}
继续测试一下:
javascript">const test1 = new MyPromise((resolve, reject) => {// 只以第一次为准resolve('success')reject('fail')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }
Throw处理
Promise中如果有throw的话 就会执行Reject操作,加上try catch包一下:
javascript">+ try {// 执行传进来的函数executor(this.resolve, this.reject)
+ } catch (e) {// 捕捉到错误直接执行reject
+ this.reject(e)
+ }
继续完善下:
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}
}
测试代码
javascript">const test3 = new MyPromise((resolve, reject) => {throw('fail')
})
console.log(test3) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
then
一般使用方式如下:
javascript">// 马上输出 ”success“
const p1 = new Promise((resolve, reject) => {resolve('success')
}).then(res => console.log(res), err => console.log(err))// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {setTimeout(() => {reject('fail')}, 1000)
}).then(res => console.log(res), err => console.log(err))// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {resolve(100)
}).then(res => 2 * res, err => console.log(err)).then(res => console.log(res), err => console.log(err))
根据上述代码可以确定:
- then接收两个回调,一个是成功回调,一个是失败回调;
- 当Promise状态为fulfilled执行成功回调,为rejected执行失败回调;
- 如resolve或reject在定时器里,则定时器结束后再执行then;
- then支持链式调用,下一次then执行受上一次then返回值的影响;
实现then函数
javascript"> then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)}}
完善一下上边的code:
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)}}
}
测试then的效果:
javascript">// 输出 ”success“
const test = new MyPromise((resolve, reject) => {resolve('success')
}).then(res => console.log(res), err => console.log(err))
定时器
如何保证执行then回调在有定时器的情况下:
javascript">// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {setTimeout(() => {reject('fail')}, 1000)
}).then(res => console.log(res), err => console.log(err))
我们不能确保1秒后才执行then函数,但是我们可以保证1秒后再执行then里的回调(后续课程的事件循环会讲到)
在这1秒时间内,我们可以先把then里的两个回调保存起来,然后等到1秒过后,执行了resolve或者reject,咱们再去判断状态,并且判断要去执行刚刚保存的两个回调中的哪一个回调。
那么问题来了,我们怎么知道当前1秒还没走完甚至还没开始走呢?其实很好判断,只要状态是pending,那就证明定时器还没跑完,因为如果定时器跑完的话,那状态肯定就不是pending,而是fulfilled或者rejected
那是用什么来保存这些回调呢?建议使用数组,因为一个promise实例可能会多次then,用数组就一个一个保存了
javascript"> initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态
+ this.onFulfilledCallbacks = [] // 保存成功回调
+ this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调
+ while (this.onFulfilledCallbacks.length) {
+ this.onFulfilledCallbacks.shift()(this.PromiseResult)
+ }}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调
+ while (this.onRejectedCallbacks.length) {
+ this.onRejectedCallbacks.shift()(this.PromiseResult)
+ }}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)
+ } else if (this.PromiseState === 'pending') {
+ // 如果状态为待定状态,暂时保存两个回调
+ this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+ this.onRejectedCallbacks.push(onRejected.bind(this))
+ }}
完整代码:
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(onFulfilled.bind(this))this.onRejectedCallbacks.push(onRejected.bind(this))}}
}
测试下定时器是否可以正常调用
javascript">const test2 = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('success') // 1秒后输出 success}, 1000)
}).then(res => console.log(res), err => console.log(err))
Promise_493">Promise的链式调用
then支持链式调用,下一次then执行受上一次then返回值的影响,给大家举个例子:
javascript">// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {resolve(100)
}).then(res => 2 * res, err => console.log(err)).then(res => console.log(res), err => console.log(err))// 链式调用 输出300
const p4 = new Promise((resolve, reject) => {resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err)).then(res => console.log(res), err => console.log(err))
根据上文,可以得到:
- then方法本身会返回一个新的Promise对象;
- 如果返回值是promise对象,返回值为成功,新promise就是成功;
- 如果返回值是promise对象,返回值为失败,新promise就是失败;
- 如果返回值非promise对象,新promise对象就是成功,值为此返回值;
then是Promise上的方法,那如何实现then完还能再then呢?
then执行后返回一个Promise对象就行了,就能保证then完还能继续执行then;
javascript"> then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {try {const x = cb(this.PromiseResult)if (x === thenPromise && x) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}
完整代码为:
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}
}
测试一下看看~
javascript">const test3 = new MyPromise((resolve, reject) => {resolve(100) // 输出 状态:success 值: 200
}).then(res => 2 * res, err => 3 * err).then(res => console.log('success', res), err => console.log('fail', err))const test4 = new MyPromise((resolve, reject) => {resolve(100) // 输出 状态:fail 值:200}).then(res => new MyPromise((resolve, reject) => reject(2 * res)), err => new Promise((resolve, reject) => resolve(3 * err))).then(res => console.log('success', res), err => console.log('fail', err))
执行顺序问题
这里需要了解,then方法是微任务(后续课程会讲)
javascript">const p = new Promise((resolve, reject) => {resolve(1)
}).then(res => console.log(res), err => console.log(err))console.log(2)输出顺序是 2 1
具体原因如下:
在 JavaScript 中,Promise 是异步执行的,而 console.log 是同步执行的。当你创建一个新的 Promise 并立即调用 resolve 方法时,Promise 的状态会立即变为 “fulfilled”,但是 Promise 的处理函数(.then 中的回调函数)会被放入微任务队列(microtask queue)中,等待当前执行栈清空后执行。
由于 **JavaScript 运行时会先执行所有同步代码,然后才处理微任务队列中的微任务,所以 console.log(2) 会先于 .then 中的回调函数执行。**因此,输出顺序是 2 然后是 1。
这是 JavaScript 事件循环(Event Loop)的一个基本特性,它确保了同步代码的即时执行,而异步代码(如 Promise 的回调函数)则在微任务队列中等待执行。
至此可以采用类似setTimeout来解决类似问题:
javascript">const resolvePromise = cb => {setTimeout(() => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}})
}
完整Code:
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {setTimeout(() => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}})}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}
}
其他方法:
all:
javascript">static all(promises) {const result = []let count = 0return new MyPromise((resolve, reject) => {const addData = (index, value) => {result[index] = valuecount++if (count === promises.length) resolve(result)}promises.forEach((promise, index) => {if (promise instanceof MyPromise) {promise.then(res => {addData(index, res)}, err => reject(err))} else {addData(index, promise)}})})
}
race
javascript">static race(promises) {return new MyPromise((resolve, reject) => {promises.forEach(promise => {if (promise instanceof MyPromise) {promise.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(promise)}})})
}
allSettled
3. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
4. 把每一个Promise的结果,集合成数组后返回;
javascript">static allSettled(promises) {return new Promise((resolve, reject) => {const res = []let count = 0const addData = (status, value, i) => {res[i] = {status,value}count++if (count === promises.length) {resolve(res)}}promises.forEach((promise, i) => {if (promise instanceof MyPromise) {promise.then(res => {addData('fulfilled', res, i)}, err => {addData('rejected', err, i)})} else {addData('fulfilled', promise, i)}})})
}
any
与all相反
javascript">static any(promises) {return new Promise((resolve, reject) => {let count = 0promises.forEach((promise) => {promise.then(val => {resolve(val)}, err => {count++if (count === promises.length) {reject(new AggregateError('All promises were rejected'))}})})})
}
}
Promise_A__973">Promise A+ 规范
上文我们实现了简版的Promise,接下来看下标准的Promise/A+的规范
官方链接
对照的翻译如下:
一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。
一个 promise 表示异步操作的最终结果。与 promise 进行交互的主要方式是通过它的方法 then。该方法通过注册回调来得到这个 promise 的最终 value ,或者为什么这个 promise 不能被 fulfilled 的 reason 。
该规范详细说明了 then 方法的行为,提供了一个可互操作的基础,因此所有符合 Promises/A+ 的 promise 实现都可以依赖该基础。尽管 Promises/A+ 组织可能偶尔会通过向后兼容的微小更改来修改此规范,以解决新发现的情况,但我们只有在仔细考虑、讨论和测试后才会进行大的或向后不兼容的更改。因此, 该规范应该被认为是十分稳定的 。
从历史上看, Promises/A+ 阐明了早期 Promises/A proposal 的条款,并将部分事实上已经实现的拓展涵盖其中,以及对某些未指定或者有问题的部分省略。
最后,Promises/A+ 规范的核心不包括:如何 create 、fulfill 或 reject promises。而是选择专注于提供可互操作的 then 方法。不过伴随规范的未来工作可能会涉及这些主题。
这里可以看到,Promises/A+ 规范目前的核心是规范 then 方法,并没有对如何实现 promise 以及如何改变 promise 的状态进行限制。
常见术语
- “prmoise” 是一个拥有符合本规范的 then 方法的对象或者函数;
- “thenable” 是一个定义了 then 方法的对象或者函数;
- “value” 是 JavaScript 的任意合法值(包括 undefined, thenable, promise);
- “exception” 是一个用 throw 语句抛出的 value ;
- “reason” 是一个表示 promise 被 rejected 的 value ;
Promise 状态:
pormise 必须是以下三个状态之一: pending, fulfilled, rejected。
- 当 promise 处于 pending 状态时:
- 可以转换到 fulfilled 或 rejected 状态;
- 当 promise 处于 fulfilled 状态时:
- 不能转换到其他状态;
- 必须有一个 value ,并且不能改变;
- 当 promise 处于 rejected 状态时:
- 不能转换到其他状态;
- 必须有 reason ,并且不能改变;
promise 必须提供一个 then 方法,能由此去访问当前或最终的 value 或者 reason 。pormise 的 then 方法, 接受两个参数
- onFulfilled 和 onRejected 都是可选参数:
- 如果 onFulfilled 不是函数,则忽略;
- 如果 onRejected 不是函数,则忽略;
- 如果 onFulfilled 是一个函数:
- 它必须在 promise 被 fulfilled 后,以 promise 的 value 作为第一个参数调用;
- 它不能在 promise 被 fulfilled 之前调用;
- 它不能被调用多次;
- 如果 onRejected 是一个函数:
- 它必须在 promise 被 rejected 后,以 promise 的 reason 作为第一个参数调用;
- 它不能能在 promise 被 rejected 之前调用;
- 它不能被调用多次;
- 在 execution context 栈(执行上下文栈)只包含平台代码之前, onFulfilled 或者 onRejected 不能被调用 (译者注: 异步执行回调);
- onFulfilled 或者 onRejected 必须以函数形式调用(即不能有this值)
- then 方法可以被同一个 promise 调用多次
- 如果或者当 promise 处于 fulfilled 状态, 所有自己的 onFulfilled 回调函数,必须要按照 then 注册的顺序被调用;
- 如果或者当 promise 处于 rejected 状态, 所有自己的 onRejected 回调函数,必须要按照 then 注册的顺序被调用;
- then 方法必须要返回 promise
-
- 如果 onFulfilled 或者 onRejected 返回一个值 x ,则执行 Promise Resolution Procedure:[[Resolve]](promise2, x);
- 如果 onFulfilled 或者 onRejected 抛出异常 e , promise2 必须以 e 作为 reason ,转到 rejected 状态;
- 如果 onFulfilled 不是函数,并且 promise1 处于 fulfilled 状态 ,则 promise2 必须以与 promise1 同样的 value 被 fulfilled;
- 如果 onRejected 不是函数,并且 promise1 处于 rejected 状态 ,则 promise2 必须以与 promise1 同样的 reason 被 rejected;
Promise Resolution Procedure:
Promise Resolution Procedure 是一个抽象操作。它以一个 promise 和一个 value 作为输入,记作:[[Resolve]](promise, x) 。 如果 x 是一个 thenable , 它会尝试让 promise 变成与 x 的一样状态 ,前提 x 是一个类似的 promise 对象。否则,它会让 promise 以 x 作为 value 转为 fulfilled 状态。
这种对 thenables 的处理允许不同的 promise 进行互操作,只要它们暴露一个符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法“同化”不一致的实现。
[[Resolve]](promise, x) 执行以下步骤:
- 如果 promise 和 x 引用的是同一个对象,则以一个 TypeError 作为 reason 让 promise 转为 rejeted 状态;
- 如果 x 也是一个 promise ,则让 promise 接受它的状态:
- 如果 x 处于 pending 状态,promise 必须保持 pending 状态,直到 x 变成 fulfilled 或者 rejected 状态,promise 才同步改变;
- 如果或者当 x 处于 fulfilled 状态, 以同样的 value 让 promise 也变成 fulfilled 状态;
- 如果或者当 x 处于 rejected 状态, 以同样的 reason 让 promise 也变成 rejected 状态;
- 如果 x 是一个对象或者函数:
- 令 then 等于 x.then;
- 如果读取 x.then 抛出异常 e , 以 e 作为 reason 让 promise 变成 rejected 状态;
- 如果 then 是一个函数,以 x 作为 this 调用它,传入第一个参数 resolvePromise , 第二个参数 rejectPromise :
- 如果 resolvePromise 被传入 y 调用, 则执行 [[Resolve]](promise, y);
- 如果 rejectedPromise 被传入 r 调用,则用,r 作为 reason 让 promise 变成 rejected 状态;
- 如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用多次了。只有第一次调用生效,其余会被忽略;
- 如果调用 then 抛出异常 e:
- 如果 resolvepromise 或 rejectPromise 已经被调用过了,则忽略它;
- 否则, 以 e 作为 reason 让 promise 变成 rejected 状态;
- 如果 then 不是一个函数,以 x 作为 value 让 promise 变成 fulfilled 状态;
- 如果 x 不是对象或函数, 以 x 作为 value 让 promise 变成 fulfilled 状态;
如果一个 promise 被一个循环的 thenable 链中的对象 resolved,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励实现者检测这样的递归是否存在,并且以 TypeError 作为 reason 拒绝 promise;
Async__Await_1050">Async & Await
async/await的用处:用同步方式,执行异步操作
javascript">function request(num) { // 模拟接口请求return new Promise(resolve => {setTimeout(() => {resolve(num * 2)}, 1000)})
}request(1).then(res1 => {console.log(res1) // 1秒后 输出 2request(2).then(res2 => {console.log(res2) // 2秒后 输出 4})
})
现在有一个新的要求:先请求完接口1,再拿接口1返回的数据,去当做接口2的请求参数,那我们也可以这么做:
javascript">request(5).then(res1 => {console.log(res1) // 1秒后 输出 10request(res1).then(res2 => {console.log(res2) // 2秒后 输出 20})
})
如果嵌套的多了,这个时候就可以用async/await来解决了:
javascript">async function fn () {const res1 = await request(5)const res2 = await request(res1)console.log(res2) // 2秒后输出 20
}
fn()
使用async/await代替上述的内容:
javascript">async function fn () {await request(1)await request(2)// 2秒后执行完
}
fn()
在async函数中,await规定了异步操作只能一个一个排队执行,从而达到用同步方式,执行异步操作的效果.
注意:await只能在async函数中使用
刚刚上面的例子await后面都是跟着异步操作Promise,那如果不接Promise?
javascript">function request(num) { // 去掉PromisesetTimeout(() => {console.log(num * 2)}, 1000)
}async function fn() {await request(1) // 2await request(2) // 4// 1秒后执行完 同时输出
}
fn()
?: 什么是async?async是一个位于function之前的前缀,只有async函数中,才能使用await。那async执行完是返回是什么
javascript">async function fn () {}
console.log(fn) // [AsyncFunction: fn]
console.log(fn()) // Promise {<fulfilled>: undefined}
可以看出,async函数执行完会自动返回一个状态为fulfilled的Promise,也就是成功状态,但是值却是undefined,那要怎么才能使值不是undefined呢?只要函数有return返回值就行了。
javascript">async function fn (num) {return num
}
console.log(fn) // [AsyncFunction: fn]
console.log(fn(10)) // Promise {<fulfilled>: 10}
fn(10).then(res => console.log(res)) // 10
总结:
- await只能在async函
- 数中使用,不然会报错;
- async函数返回的是一个Promise对象,有无值看有无return值;
- await后面最好是接Promise,虽然接其他值也能达到排队效;
- async/await作用是用同步方式,执行异步操作
Q:async/await是一种语法糖,那么什么是语法糖呢?
A:语法糖是简化代码的一种方式,用其他方式也能达到同样的效果,但写法可能没有这么便利。套了一层外衣
ES6的class也是语法糖,因为其实用普通function也能实现同样效果
回归正题,async/await是一种语法糖,用到的是ES6里的迭代函数——generator函数
Generator_1147">Generator:
介绍
generator函数跟普通函数在写法上的区别就是,多了一个星号*,并且只有在generator函数中才能使用yield,而yield相当于generator函数执行的中途暂停点,比如下方有3个暂停点。而怎么才能暂停后继续走呢?那就得使用到next方法,next方法执行后会返回一个对象,对象中有value 和 done两个属性
- value:暂停点后面接的值,也就是yield后面接的值;
- done:是否generator函数已走完,没走完为false,走完为true;
javascript">function* gen() {yield 1yield 2yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
可以看到最后一个是undefined,这取决于你generator函数有无返回值
javascript">function* gen() {yield 1yield 2yield 3return 4
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: 4, done: true }
Yeild 后接函数:
yield后面接函数的话,到了对应暂停点yield,会马上执行此函数,并且该函数的执行返回值,会被当做此暂停点对象的value
javascript">function fn(num) {console.log(num)return num
}
function* gen() {yield fn(1)yield fn(2)return 3
}
const g = gen()
console.log(g.next())
// 1
// { value: 1, done: false }
console.log(g.next())
// 2
// { value: 2, done: false }
console.log(g.next())
// { value: 3, done: true }
yield后接promise
javascript">function fn(num) {return new Promise(resolve => {setTimeout(() => {resolve(num)}, 1000)})
}
function* gen() {yield fn(1)yield fn(2)return 3
}
const g = gen()
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: 3, done: true }
如果想获取的是两个Promise的结果1 和 2,可以使用Promise的then
javascript">const g = gen()
const next1 = g.next()
next1.value.then(res1 => {console.log(next1) // 1秒后输出 { value: Promise { 1 }, done: false }console.log(res1) // 1秒后输出 1const next2 = g.next()next2.value.then(res2 => {console.log(next2) // 2秒后输出 { value: Promise { 2 }, done: false }console.log(res2) // 2秒后输出 2console.log(g.next()) // 2秒后输出 { value: 3, done: true }})
})
Next传递参数
generator函数可以用next方法来传参,并且可以通过yield来接收这个参数,注意两点
- 第一次next传参是没用的,只有从第二次开始next传参才有用;
- next传值时,要记住顺序是,先右边yield,后左边接收参数;
javascript">function* gen() {const num1 = yield 1console.log(num1)const num2 = yield 2console.log(num2)return 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next(11111))
// 11111
// { value: 2, done: false }
console.log(g.next(22222))
// 22222
// { value: 3, done: true }
Promisenext_1263">Promise&next传参
根据上文可以知道:
- yield后面接Promise;
- next函数传参;
所以一起使用时的效果为:
javascript">function fn(nums) {return new Promise(resolve => {setTimeout(() => {resolve(nums * 2)}, 1000)})
}
function* gen() {const num1 = yield fn(1)const num2 = yield fn(num1)const num3 = yield fn(num2)return num3
}
const g = gen()
const next1 = g.next()
next1.value.then(res1 => {console.log(next1) // 1秒后同时输出 { value: Promise { 2 }, done: false }console.log(res1) // 1秒后同时输出 2const next2 = g.next(res1) // 传入上次的res1next2.value.then(res2 => {console.log(next2) // 2秒后同时输出 { value: Promise { 4 }, done: false }console.log(res2) // 2秒后同时输出 4const next3 = g.next(res2) // 传入上次的res2next3.value.then(res3 => {console.log(next3) // 3秒后同时输出 { value: Promise { 8 }, done: false }console.log(res3) // 3秒后同时输出 8// 传入上次的res3console.log(g.next(res3)) // 3秒后同时输出 { value: 8, done: true }})})
})
实现async await
上方的generator函数的Promise&next传参,就很像async/await了,区别在于
- gen函数执行返回值不是Promise,asyncFn执行返回值是Promise;
- gen函数需要执行相应的操作,才能等同于asyncFn的排队效果;
- gen函数执行的操作是不完善的,因为并不确定有几个yield,不确定会嵌套几次;
针对这种情况,可以通过高阶函数(HOC)封装:
高阶函数:参数是函数,返回值也可以是函数。
javascript">function highorderFn(函数) {// 一系列处理return 函数
}
根据上述代码,可以封装一个高阶函数,接收一个generator函数,并经过处理,返回一个具有async函数功能的函数:
function generatorToAsync(generatorFn) {// 经过一系列处理return 具有async函数功能的函数
}
返回值是Promise:
function* gen() {}const asyncFn = generatorToAsync(gen)console.log(asyncFn()) // 期望这里输出 Promise
javascript">function* gen() {}
function generatorToAsync (generatorFn) {return function () {return new Promise((resolve, reject) => {})}
}const asyncFn = generatorToAsync(gen)console.log(asyncFn()) // Promise
结合以上代码
把之前的处理代码,加入generatorToAsync函数中
javascript">function fn(nums) {return new Promise(resolve => {setTimeout(() => {resolve(nums * 2)}, 1000)})
}
function* gen() {const num1 = yield fn(1)const num2 = yield fn(num1)const num3 = yield fn(num2)return num3
}
function generatorToAsync(generatorFn) {return function () {return new Promise((resolve, reject) => {const g = generatorFn()const next1 = g.next()next1.value.then(res1 => {const next2 = g.next(res1) // 传入上次的res1next2.value.then(res2 => {const next3 = g.next(res2) // 传入上次的res2next3.value.then(res3 => {// 传入上次的res3resolve(g.next(res3).value)})})})})}
}const asyncFn = generatorToAsync(gen)asyncFn().then(res => console.log(res)) // 3秒后输出 8
到这里,就已经实现了async/await的初始功能了
javascript">async function asyncFn() {const num1 = await fn(1)const num2 = await fn(num1)const num3 = await fn(num2)return num3
}
asyncFn().then(res => console.log(res)) // 3秒后输出 8
因为async中可以支持若干个await,await的个数是不确定的。同样类比,generator函数中,也可能有2个yield,3个yield,5个yield,所以需要对上述代码进行改造
javascript">function generatorToAsync(generatorFn) {return function() {const gen = generatorFn.apply(this, arguments) // gen有可能传参// 返回一个Promisereturn new Promise((resolve, reject) => {function go(key, arg) {let restry {res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise} catch (error) {return reject(error) // 报错的话会走catch,直接reject}// 解构获得value和doneconst { value, done } = resif (done) {// 如果done为true,说明走完了,进行resolve(value)return resolve(value)} else {// 如果done为false,说明没走完,还得继续走// value有可能是:常量,Promise,Promise有可能是成功或者失败return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))}}go("next") // 第一次执行})}
}const asyncFn = generatorToAsync(gen)asyncFn().then(res => console.log(res))
测试结果:
javascript">async function asyncFn() {const num1 = await fn(1)console.log(num1) // 2const num2 = await fn(num1)console.log(num2) // 4const num3 = await fn(num2)console.log(num3) // 8return num3
}
const asyncRes = asyncFn()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8
javascript">function* gen() {const num1 = yield fn(1)console.log(num1) // 2const num2 = yield fn(num1)console.log(num2) // 4const num3 = yield fn(num2)console.log(num3) // 8return num3
}const genToAsync = generatorToAsync(gen)
const asyncRes = genToAsync()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8