静态函数
Promise.all()
then()函数和catch()函数是Promise原型链中的函数,因此每个Promise的实例可以进行共享,而all()函数是Promise本身的静态函数,用于将多个Promise实例包装成一个新的Promise实例。
const p1 = new Promise((resolve, reject) => {
resolve('success');
}).then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error('error');
// 由于p2有自己的catch()函数,所以这个异常会在p2实例内部被消化,并不会继续向外抛到Promise.all()函数中。
}).then(result => result)
.catch(
// p2实例执行完catch()函数后,p2的状态实际是变为fulfilled,只不过它的返回值是Error的信息。
e => e);
Promise.all([p1, p2])
.then(result => console.log(result)) // ['success', Error: error]
.catch(e => console.log(e));
新Promise实例p的状态由p1、p2共同决定,总共会出现以下两种情况:
- 只有p1、p2全部的状态都变为fulfilled成功状态,p的状态才会变为fulfilled状态,此时p1、p2的返回值组成一个数组,作为p的then()函数的回调函数的参数。
- 只要p1、p2中有任意一个状态变为rejected失败状态,p的状态就变为rejected状态,此时第一个被reject的实例的返回值会作为p的catch()函数的回调函数的参数。
需要注意的是,如果p1、p2中已经定义了catch()函数,那么当其中一个Promise状态变为rejected时,并不会触发Promise.all()函数的catch()函数。
Promise.race()
Promise.race()函数作用于多个Promise实例上,返回一个新的Promise实例,表示的是如果多个Promise实例中有任何一个实例的状态发生改变,那么这个新实例的状态就随之改变,而最先改变的那个Promise实例的返回值将作为新实例的回调函数的参数。
// 实现一个场景:假如发送一个Ajax请求,在5秒后还没有收到请求成功的响应时,会自动处理成请求失败。
const p1 = ajaxGetPromise('/testUrl');
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
});
const p = Promise.race([p1, p2]);
p.then(res => console.log(res)).catch(e => console.error(e));
Promise.resolve()
Promise.resolve()函数将传入的变量返回为一个状态为fulfilled的Promise实例,等价于在Promise函数体内调用resolve()函数。Promise.resolve()函数执行后,Promise的状态会立即变为fulfilled,然后进入then()函数中做处理。
const p = Promise.resolve('hello');
// 等价于
const p = new Promise(resolve => resolve('hello'));
Promise.reject()
Promise.reject()函数用于返回一个状态为rejected的Promise实例,函数在执行后Promise的状态会立即变为rejected,从而会立即进入catch()函数中做处理,等价于在Promise函数体内调用reject()函数。
const p = Promise.reject('出错了');
// 等价于
const p = new Promise((resolve, reject) => reject('出错了'));
注意点
- then()函数返回的是一个新Promise实例,因此可以使用链式调用then()函数
- 在上一轮then()函数内部return的值会作为下一轮then()函数接收的参数值。
- 在then()函数中不能返回Promise实例本身,否则会出现Promise循环引用的问题,抛出异常。
resolve或reject后,后面代码还会执行,建议加return;但后面代码如果还是resolve()函数和reject()函数,则不会执行,因为一个Promise实例只能执行一次状态变更
p().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
p1().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
p2().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
function p() {
return new Promise((resolve, reject) => {
reject('p reject')
console.log('p after') // 这边代码也会执行
setTimeout(() => {
console.log('p setTimeout')
}, 2000)
})
}
function p1() {
return new Promise((resolve, reject) => {
resolve('p1 resolve')
console.log('p1 after') // 这边代码也会执行
})
}
function p2() {
return new Promise((resoleve, reject) => {
return reject(new Error('p2error'))
console.log('p2 after') // 这边代码不会执行
})
}
function p3() {
return new Promise((resoleve, reject) => {
resolve('success1')
reject('error') // 不会执行
resolve('success2') // 不会执行
})
}
重复调用同一个Promise,会一起执行。因为同一个Promise的实例只能有一次状态变换的过程,在状态变换完成后,如果成功会触发所有的then()函数,如果失败会触发所有的catch()函数。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
const start = Date.now()
// 重复调用实例p的then()函数
p.then((res) => {
// 得到的结果不同也是正常情况,这取决于运行的环境,很可能会相差几毫秒。
console.log(res, Date.now() - start) // success 1004
})
p.then((res) => {
console.log(res, Date.now() - start) // success 1004
})
throw和return一个Error的区别:
return new Error()
是返回一个Error实例,不会被catch捕获;throw new Error()
抛出一个Error,才会被catch捕获Promise.resolve()
.then(() => {
// 返回的是一个Error实例,并将Error实例作为参数传递给第二个then
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res) // then: Error: error!!!
})
.catch((err) => {
// 不会执行catch
console.log('catch: ', err)
})
then()函数接收的参数不是一个规范的函数形式时(即形如(res) => {} ),会发生值穿透现象,即传递的值会被直接忽略掉,继续执行链式调用后续的函数。
Promise.resolve(1)
.then(2) // 参数为2,被忽略
.then(Promise.resolve(3)) // 参数为Promise,不是需要的函数形式,被忽略
.then(console.log) // 1
推荐用catch()函数处理异常,而不要用then()函数的第二个参数去处理:因为then()函数的第二个函数不能捕获第一个函数中抛出的异常,而catch()函数却能捕获到第一个函数中抛出的异常。
Promise.resolve()
.then(function success(res) {
throw new Error('error')
}, function fail1(e) {
// 捕获不到第一个参数抛出的异常
console.error('fail1: ', e)
})
.catch(function fail2(e) {
console.error('fail2: ', e)
})
封装原生Ajax
// 封装原生get类型Ajax请求
function ajaxGetPromise(url) {
return new Promise((resolve, reject) => {
// 原生Ajax请求操作
const XHR = new XMLHttpRequest()
XHR.open("GET", url)
XHR.onreadystatechange = function () {
//readyState属性表示请求/响应过程的当前活动阶段。
if (XHR.readyState == 4) {
if ((XHR.status >= 200 && XHR.status < 300) || XHR.status == 304) {
try {
//获取数据
let response = JSON.parse(XHR.responseText)
resolve(response)
} catch (e) {
reject(e)
}
} else {
reject(new Error(XHR.statusText))
}
}
}
XHR.responseType = "json"
XHR.setRequestHeader("Accept", "application/json")
XHR.send()
})
}
实现Promise
```javascript const PENDING = ‘pending’ const FULFILLED = ‘fulfilled’ const REJECTED = ‘rejected’
class myPromise {
constructor(executor) { // executor 是一个执行器,进入会立即执行 // 并传入resolve和reject方法 try { executor(this.resolve, this.reject) } catch (error) { // 如果有错误,就直接执行 reject this.reject(error) } }
value = null error = null status = PENDING // 存储成功回调函数 preserveFulCbs = [] // 存储失败回调函数 preserveRejCbs = []
resolve = (value) => { if (this.status === PENDING) { this.status = FULFILLED this.value = value // resolve里面将所有成功的回调拿出来执行 while (this.preserveFulCbs.length) { // Array.shift() 取出数组第一个元素调用,取出后,数组将失去该元素,直到数组为空 this.preserveFulCbs.shift()(value) } } }
reject = (error) => { if (this.status === PENDING) { this.status = REJECTED this.error = error // reject里面将所有失败的回调拿出来执行 while (this.preserveRejCbs.length) { this.preserveRejCbs.shift()(error) } } }
then(onSuccess, onError) { // 如果不传,就使用默认函数 onSuccess = typeof onSuccess === ‘function’ ? onSuccess : value => value onError = typeof onError === ‘function’ ? onError : error => { throw error }
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new myPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
// then 执行的时错误捕获
try {
// 获取成功回调函数的执行结果
const x = onSuccess(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === REJECTED) {
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onError(this.error)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.status === PENDING) {
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功/失败函数的时候再传递
this.preserveFulCbs.push(() => {
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onSuccess(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.preserveRejCbs.push(() => {
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onError(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})
return promise2
}
// resolve 静态方法 static resolve(parameter) { // 如果传入 myPromise 就直接返回 if (parameter instanceof myPromise) { return parameter }
// 转成常规方式
return new myPromise(resolve => {
resolve(parameter)
})
}
// reject 静态方法 static reject(reason) { return new myPromise((resolve, reject) => { reject(reason) }) } }
function resolvePromise(promise, x, resolve, reject) { // 如果相等了,说明return的是自己,抛出类型错误并返回 if (promise === x) { return reject(new TypeError(‘The promise and the return value are the same’)) }
if (typeof x === ‘object’ || typeof x === ‘function’) { // x 为 null 直接返回,走后面的逻辑会报错 if (x === null) { return resolve(x) }
let then
try {
// 把 x.then 赋值给 then
then = x.then
} catch (error) {
// 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promise
return reject(error)
}
// 如果 then 是函数
if (typeof then === 'function') {
let called = false
try {
then.call(
x, // this 指向 x
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量 called
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return
called = true
reject(r)
})
} catch (error) {
// 如果调用 then 方法抛出了异常 error:
// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
if (called) return
// 否则以 error 为据因拒绝 promise
reject(error)
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x)
}
} else { // 如果 x 不为对象或者函数,以 x 为参数执行 promise resolve(x) } }
let promise = new myPromise((resolve, reject) => { resolve(‘成功’) // reject(‘失败’) // setTimeout(() => { // resolve(‘成功’) // }, 2000) // throw new Error(‘执行器错误’) })
// promise.then(value => { // console.log(‘1’, value) // }, error => { // console.log(‘reject’, error) // })
// promise.then(value => { // console.log(‘2’, value) // }, error => { // console.log(‘reject’, error) // })
// promise.then(value => { // console.log(‘3’, value) // }, error => { // console.log(‘reject’, error) // })
// promise.then(value => { // console.log(1) // console.log(‘resolve’, value) // return new myPromise((resolve, reject) => { // resolve(‘other’) // }) // }).then(value => { // console.log(2) // console.log(‘resolve’, value) // })
// const p1 = promise.then(value => { // console.log(1) // console.log(‘resolve’, value) // return p1 // })
// // 运行的时候会走reject // p1.then(value => { // console.log(2) // console.log(‘resolve’, value) // }, reason => { // console.log(3) // console.log(reason.message) // })
// 第一个then方法中的错误要在第二个then方法中捕获到 // promise.then(value => { // console.log(1) // console.log(‘resolve’, value) // throw new Error(‘then error’) // }, reason => { // console.log(2) // console.log(reason.message) // }).then(value => { // console.log(3) // console.log(value) // }, reason => { // console.log(4) // console.log(reason.message) // })
// promise.then().then().then(value => console.log(value))
myPromise.resolve().then(() => { console.log(0) return myPromise.resolve(4) }).then((res) => { console.log(res) })
myPromise.resolve().then(() => { console.log(1) }).then(() => { console.log(2) }).then(() => { console.log(3) }).then(() => { console.log(5) }).then(() => { console.log(6) }) ```