本文参考了此博客,在网上浏览了很多关于Promise的相关资料,promise可以说是JavaScript的重难点了,其中,很是感谢阮一峰老师的博客讲解以及MDN上的文档教程
如对Promise不太熟悉的同学,可以先浏览Promise工作原理分析,如有不同意见,欢迎讨论,一起进步。
Promise基本流程
function doSomething(){
var p1 = new Promise(function (resolve, reject) {
setTimeout(()=>{
//setTimeout1
console.log('Start do something')
resolve('Success!')
},1000)
})
return p1
}
function successCallback(data){
var p2= new Promise(function (resolve, reject) {
setTimeout(()=>{
//setTimeout2
console.log(data)
resolve()
},1000)
})
return p2
}
function failCallback(data){
var p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('Error1!')
resolve() //setTimeout3
},1000)
})
return p3
}
//接下来去调用一下
doSomething() //promise1
.then(
(data)=>{
//promise2
return successCallback(data)},
(data)=>{
return failCallback(data)}
)
.then(()=>{
//promise3
return new Promise((resolve,rejected)=>{
rejected('Unknown Error')
})
})
.catch(res=>{
//promise4
console.log(res)
})
输出:
Start do something
Success!
Unknown Error
过程:
- 我们来看一看这个promise链,首先执行了doSomething(),此方法返回了p1,并且在p1刚建立之时,此promise链就开始执行。
- 当setTimeout1执行完之后(即setTimeout1中的resolve(‘Success!’)执行之后),p1的状态会由pending->fulfilled,然后调用then()方法,因为promise1已经fulfilled,所以,这里将调用then()方法中的第一个函数对象,在第一个函数对象中执行了successCallback(data)并返回了promise2。
- 此时promise2的状态为pending,在setTimeout2中执行了 resolve()之后,其状态会由pending->fulfilled。
- 然后再调用then()方法,这时候会生成promise3,其状态被设置为rejected,该异常会沿着promise链向下传递,最终被catch()捕获处理。
构造函数Promise必须接受一个函数对象作为参数,我们称其为handle,handle又包含了resolve和reject两个参数,这两个参数都是函数对象。
定义一个判断变量是否为函数的方法,后面会用到:
// 判断变量否为function
const isFunction = variable => typeof variable === 'function'
MyPromise实现promise功能
1. constructor
首先我们定义一个MyPromise的class,它接收一个函数handle作为参数
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
}
}
我们从上方的过程中可以知道,promise主体执行异步程序,然后根据是否完成异步程序设置promise的状态,然后根据promise的状态调用then()方法。
所以,我们也为MyPromise添加一个状态标识
- Pending(进行中)
- Fulfilled(已成功)
- Rejected(已失败)
状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。
2. resolve和reject
resolve和reject做了什么?
- resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)
- reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
- resolve 和 reject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
为MyPromise添加状态和值,并添加状态改变的执行逻辑:
注意: MyPromise的状态只能改变一次,即状态改变之前MyPromise的状态只能是PENDING。
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = PENDING
// 添加状态
this._value = undefined
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
this._status = FULFILLED
this._value = val
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
}
}
这里我们需要理解:
handle(this._resolve.bind(this),this._reject.bind(this))
我之前阅读这一句代码时陷入了思想误区,这里,我将详细展开为什么要这样写。
观察如下代码:
class myTest{
constructor(handle) {
this._sum = undefined
handle(this._add.bind(this))
}
_add(a,b){
this._sum = a + b
return this._sum
}
}
let test = new myTest((doSomething)=>{
let [a, b] = [1, 2]
console.log(doSomething(a,b))
})
在创建myTest时,传入了一个箭头函数:
(doSomething)=>{
let [a, b] = [1, 2];console.log(doSomething(a,b))}
这个箭头函数就是myTest中的handle,然后调用了handle(this._add.bind(this)),这里向handle传入的参数为this._add函数对象,也就是说,这个箭头函数中的doSomething被赋值为了this._add。
平时我们常见到的参数赋值都是实例变量的赋值,而这里是函数变量的赋值,所以有一点难以理解,但是其实和实例变量赋值是一样的思想。
这里还涉及了this指向的知识点,bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。如有疑惑可以查看我的这一篇博客
3. then()
观察promise中的then()
我们已经知道promise的then()方法会接收两个函数对象作为参数,分别对应着promise状态为onFulfilled和onRejected的回调函数
promise.then(onFulfilled, onRejected)
- 如果onFulfilled或onRejected不是函数,其必须忽略。
- then() 方法会返回一个新的promise对象,因此promise支持链式调用:
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
- 如果onFulfilled1或onRejected1