静态函数
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实例作为参数传递给第二个thenreturn new Error('error!!!')}).then((res) => {console.log('then: ', res) // then: Error: error!!!}).catch((err) => {// 不会执行catchconsole.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 thentry {// 把 x.then 赋值给 thenthen = x.then} catch (error) {// 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promisereturn reject(error)}// 如果 then 是函数if (typeof then === 'function') {let called = falsetry {then.call(x, // this 指向 x// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)y => {// 如果 resolvePromise 和 rejectPromise 均被调用,// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用// 实现这条需要前面加一个变量 calledif (called) returncalled = trueresolvePromise(promise, y, resolve, reject)},// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promiser => {if (called) returncalled = truereject(r)})} catch (error) {// 如果调用 then 方法抛出了异常 error:// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回if (called) return// 否则以 error 为据因拒绝 promisereject(error)}} else {// 如果 then 不是函数,以 x 为参数执行 promiseresolve(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) }) ```
