一、什么是Promise?
Promise是js异步编程的解决方案。从语法上看,Promise是一个构造函数;从功能上看,promise对象用来封装一个异步操作并可以获取其结果;
每个Promise实例对象都有三个状态,分别为pending(初始,状态未改变)、fulfilled(成功)、rejected(失败);
promise对象的状态只能从pending到fulfilled或从pending到rejected变化,并且状态改变后不再改变;
二、为什么使用Promise?
Promise指定回调函数的方式或时机更灵活
在传统回调函数中,必须在异步任务启动前指定回调函数; 而在使用Promise时,我们通常先启动异步任务,然后返回一个promise对象,最后通过then/catch指定回调函数(甚至可以在异步任务结束后指定回调函数);
javascript
/* 传统回调函数 */
$.ajax({
url: '/user/info',
method: 'post',
success: function() {
// 请求成功回调,需要在在开启请求之前指定
}
})
/* promise指定回调 */
// 1. 开启异步任务,返回一个promise对象
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 200);
})
// 2. 指定回调函数
promise.then(
value => {console.log('resolve', value)},
reason => {console.log('reject', reason)}
)
// 3.异步任务结束后指定回调函数
setTimeout(() => {
promise.then(
value => {console.log('resolve', value)},
reason => {console.log('reject', reason)}
)
}, 2000)
Promise支持链式调用,可以解决回调地狱问题
什么是回调地狱? 回调函数嵌套调用,上一个回调函数返回的结果是嵌套回调函数执行的条件; 回调地狱的问题? 不便于开发者阅读,不便于异常排查; 解决方案?Promise链式调用; js异步任务的终极解决方案? async/await;
三、理解Promise的一些关键性问题
如何改变promies的状态?
上面已经提到了promise的状态有三种,pending、fulfilled、rejected; 一般说到改变状态我们都会想到resolve()和reject()这两个方法,其实还有一种改变状态的方式,这里总结一下:
- 使用resolve() 状态由pending变为fulfilled;
- 使用reject() 状态由pending变为rejected;
- throw Error('error')/ throw 任意值 状态由pending;
一个promise实例指定多个成功/失败的回调,都会被调用吗?
当promise状态改变时,对应的回调函数都会被调用;
javascript
const promise = new Promise((resolve, reject) => {
resolve(1) // 触发所有成功状态的回调函数
})
// 指定第一个回调
promise.then(
value => {console.log(value)}, // 输出1
reason => {console.log(reason)}
)
// 指定第二个回调
promise.then(
value => {console.log(value)}, // 输出1
reason => { console.log(reason) }
)
改变promise状态和指定回调函数谁先谁后?
都有可能,正常情况下先指定回调函数再改变状态,但也可以先改变状态再指定回调函数; 如何先改变状态再指定回调函数?
- 在执行器中同步调用resolve()/reject();
- 延迟更长的时间调用then,例如使用定时器; 什么时候才能得到数据?当调用回调函数时,都可以在回调函数中得到数据;
promise.then()返回的新promise对象的状态由谁决定?
总结:由then指定的回调函数(成功/失败)的结果决定; 具体:
- 如果抛出异常,新promise对象的状态变为rejected,结果是抛出的异常
- 如果返回的是非promise的任意值,新promise对象的状态变为fulfilled,结果是当前任意值;
- 如果返回另一个promise对象,此promise对象的状态及结果就会成为新promise的状态和结果;
javascript
new Promise((resolve, reject) => {
resolve(1) // 输出顺序:1 => 3 => 5
// reject(1) // 输出顺序:2 => 4 => 5
}).then(
value => {
console.log('onResolve1', value) // 顺序1
return Promise.resolve(value + 2) // 返回一个新的promise,且状态为fulfilled
},
reason => {
console.log('onReject1', reason) // 顺序2
throw 3 // 抛出异常
}
).then(
value => { console.log('onResolve2', value) // 顺序3 },
reason => { console.log('onReject2', reason) // 顺序4 }
).then(
value => { console.log('onResolve3', value) // 顺序5 },
reason => { console.log('onReject3', reason) // 顺序6 }
)
Promise怎么串联多个操作任务?
promise.then()返回一个新的promise对象,通过then的链式调用,可以串联多个同步/异步任务; 其中串联异步任务需要new一个promise对象;
javascript
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000);
}).then(value => {
console.log('任务1的结果:', value)
console.log('执行任务2(同步)')
return 2
}).then(value => {
console.log('任务2的结果: ', value)
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 2000);
})
}).then(value => {
console.log('任务3的结果', value)
})
Promise的异常传透怎么实现的?
当我们使用then的链式调用时,通常会在调用链的最后指定失败的回调catch; 当catch前面的then中有一环出现了异常或者状态变为rejected,都会通过then依次传递到catch中,注意并不是直接跳到catch; 因为我们统一用catch做异常处理了,所以一般我们不在then中写rejected回调,这时rejected的回调默认为 reason => { throw reason };
javascript
new Promise((resolve, reject) => {
// resolve(1)
reject(1)
}).then(
value => {
console.log('onResolve1', value)
return 2
},
reason => { throw reason } // 失败回调的默认值, 将异常传透给catch
).then(
value => { console.log('onResolve2', value) }
).catch(
reason => { console.log('onReject', reason) }
)
怎么中断promise链?
在回调函数中返回一个状态为pending的promise,可以中断promise链;
javascript
new Promise((resolve, reject) => {
// resolve(1)
reject(1)
}).then(
value => { console.log('onResolve1', value) }
).catch(reason => {
console.log('onReject', reason)
return new Promise(() => {}) // 返回一个pending状态的promise,中止promise链
}).then(
value => { console.log('onResolve3', value) }
)
四、源码实现
javascript
(function() {
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
/*
Promise类
excutor:执行器函数(同步)
*/
class Promise {
constructor(excutor) {
const _this = this
this.status = PENDING // promise实例对象的状态,初始值为pending
this.data = undefined // 存储结果数据的属性
this.callbacks = [] // 回调函数,每个元素的数据解构: { onResolved() {}, onRejected() {} }
// 触发成功回调
function resolve(value) {
// 当前状态不是pending,结束
if (_this.status !== PENDING) return false
// 修改当前状态为fulfilled
_this.status = FULFILLED
// 保存数据结果
_this.data = value
// 如果由待执行的回调函数,立即异步执行回调函数的onResolved
if (_this.callbacks.length) {
setTimeout(() => {
_this.callbacks.forEach(callbackObj => {
callbackObj.onResolved(value)
})
})
}
}
// 触发失败回调
function reject(reason) {
// 当前状态不是pending,结束
if (_this.status !== PENDING) return false
// 修改当前状态为rejected
_this.status = REJECTED
// 保存数据结果
_this.data = reason
// 如果有待执行的回调函数,立即异步执行回调函数的onRejected
if (_this.callbacks.length) {
setTimeout(() => {
_this.callbacks.forEach(callbackObj => {
callbackObj.onRejected(reason)
})
})
}
}
// 处理执行器异常
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
}
/*
then方法
指定成功和失败的回调
返回一个promise
*/
then(onResolved, onRejected) {
const _this = this
onResolved = typeof onResolved === 'function' ? onResolved : value => value // 向下传递value
// 指定默认的失败回调onRejected(实现异常传透的关键)
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
return new Promise((resolve, reject) => {
// 调用指定回调函数处理,根据执行结果,改变return的promise的状态
function handle(callback) {
/*
1. 如果抛出异常,return的Promise的结果就是失败的,reason就是error
2. 如果返回的结果不是promise类型,return的Promise的结果就是成功的,value就是该返回的结果
3. 如果是promise,return的Promise的结果就是该promise的结果
*/
try {
const result = callback(_this.data)
if (result instanceof Promise) {
// 3. 返回的结果是promise,return的Promise的结果就是该promise的结果
result.then(
value => resolve(value),
reason => reject(reason)
)
// result.then(resolve, reject)
} else {
// 2. 返回的结果不是promise类型,return的Promise的结果就是成功的,value就是该返回的结果
resolve(result)
}
} catch (error) {
// 1. 抛出异常,return的Promise的结果就是失败的,reason就是error
reject(error)
}
}
// 状态还没改变,先将回调函数存储起来,用于状态改变时调用
if (this.status === PENDING) {
this.callbacks.push({
onResolved () {
handle(onResolved)
},
onRejected () {
handle(onRejected)
}
})
} else if (this.status === FULFILLED) {
// 状态是fulfilled,异步执行成功回调
setTimeout(() => {
handle(onResolved)
})
} else {
// 状态是rejected,异步执行失败回调
setTimeout(() => {
handle(onRejected)
})
}
})
}
/*
catch方法
指定失败的回调
返回一个promise
*/
catch(onRejected) {
return this.then(undefined, onRejected)
}
/*
Promise的静态方法resolve
返回一个指定value的成功/失败的promise
*/
static resolve = function(value) {
return new Promise((resolve, reject) => {
// value是一个promise
if (value instanceof Promise) {
value.then(resolve, reject)
} else { // value不是一个promise
resolve(value)
}
})
}
/*
Promise的静态方法reject
返回一个指定reason的失败的promise
*/
static reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
/*
Promise的静态方法all
返回一个promise,当多有promise都成功时才成功,有一个失败则失败
*/
static all = function(promises) {
// 定义一个指定长度的数组,用于存储resolve的值
const values = new Array(promises.length)
// 计数,用于记录成功的promise次数
let resolveCount = 0
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
Promise.resolve(p).then(
value => {
values[index] = value
resolveCount ++
// 如果全部都成功了,将返回的promise改为成功
if (resolveCount === promises.length) {
resolve(values)
}
},
// 有一个promise失败,将返回的promise改为失败
reason => {
reject(reason)
}
)
})
})
}
/*
Promise的静态方法race
返回一个promise,其结果由第一 个完成的promise决定
*/
static race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
Promise.resolve(p).then(
// 有一个promise成功,将返回的promise改为成功
value => {
resolve(value)
},
// 有一个promise失败,将返回的promise改为失败
reason => {
reject(reason)
}
)
})
})
}
}
// 暴露Promise
window.Promise = Promise
})()