Promise 是 ES6 推出的用于解決callback嵌套層級太深問題的一種異步方案.
基本使用如下.
let myPromise = new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("true") // reject("false")
},1000)
})
myPromise.then(data=>{
console.log(data)
},err=>{
console.log(err)
})
先不考慮那么多,也不像很多博客里寫的那種,一上來就給非常完美的思路和非常標(biāo)準(zhǔn)的 Promise/A+ 規(guī)范.
我就按照自己思路一點(diǎn)點(diǎn)的來做.
step 01 從代碼代用級別摸清內(nèi)部構(gòu)造
- 一個(gè) Promise 對象,有三個(gè)狀態(tài).
- 初始狀態(tài)是 pending.
- 成功狀態(tài)是 resolve.
- 失敗狀態(tài)是 reject.

然后看上述寫的代碼.
let myPromise = new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("true") // reject("false")
},1000)
})
- new Promise () 是一個(gè)構(gòu)造函數(shù),沒什么好說的.
function MyPromise () {
}
// 肯定是
let my_promise = new MyPromise()
- 這個(gè)構(gòu)造函數(shù)接受的參數(shù)也是一個(gè)函數(shù),也沒什么好說的.
function MyPromise (func) {
typeof func === 'function' ===> true
}
- 在這個(gè)函數(shù)參數(shù)里我們聲明了 resolve 和 reject 兩個(gè)形參.
function MyPromise((resolve,reject) => {
typeof reject === 'function' // true
typeof resolve === 'function' // true
})
- 在后續(xù)的使用參數(shù)的過程中,我們可以知道,是構(gòu)造函數(shù)傳入的執(zhí)行器,在內(nèi)部調(diào)用了這兩個(gè)reject 和 resolve
function MyPromise() {
//xxx
}
new MyPromise((resolve,reject) => {
setTimeout(() => {
reslove(data) // or reject (err)
},1000)
})
- reject 和 resolve 肯定不是由傳遞進(jìn)去的參數(shù)自己提供的.
function MyPromise() {
//xxx
}
new MyPromise((resolve,reject) => {
setTimeout(() => {
// resovle 哪來的?
reslove(data) // or reject (err) // reject 哪來的?
},1000)
})
- 那么就只有一個(gè)可能,那是 Promise 內(nèi)部提供的兩個(gè)功能函數(shù).
function MyPromise () {
function resolve () {}
function reject () {}
}
- 其中 resolve 函數(shù)別的不管,起碼應(yīng)該會把當(dāng)前 promise 對象的狀態(tài)由 pending --> resolve reject 同理, 從 pending --> reject
function MyPromise () {
function resolve () {
// pending ---> resolve
}
function reject () {
// pending ---> reject
}
}
- 調(diào)用 then 實(shí)例方法,會將兩種狀態(tài)的回調(diào)函數(shù)傳遞進(jìn)去.
new MyPromise(executor).then(resolveData=>{},rejectData=>{})
step 02 根據(jù)上述推理,猜測內(nèi)部寫法.
1. 首先是一個(gè)構(gòu)造函數(shù)
function MyPromise () {
}
2. 其次需要接受一個(gè)執(zhí)行器.
function MyPromise (executor) {
}
3. 實(shí)例化出來的 Promise 對象有三種狀態(tài)
MyPromise.prototype.states = {
PENDING: 'PENDING',
RESOLVE: 'RESOLVE',
REJECT: 'REJECT'
}
4. 一開始 promise 對象是 pending 狀態(tài)
function MyPromise (executor) {
this.state = this.states.PENDING // pending.
}
5. 內(nèi)部起碼得有兩個(gè) reject 和 resolve.要不然executor里面的reject 和 resolve 哪來的? 或者把這兩個(gè)方法
function MyPromise (executor) {
this.state = this.states.PENDING // pending.
const that = this // 掛載 this
function resolve () {
if (that.state === this.states.PENDING) {
that.state = this.states.PENDING
}
}
function reject () {
if (that.state === this.states.PENDING) {
that.state = this.states.REJECT
}
}
}
或者覺得掛載 this 麻煩,我就把 resolve 和 reject 掛在 prototype 上.
MyPromise.prototype.reject = function () {
if (this.state === this.states.PENDING) {
this.state = this.states.REJECT
}
}
MyPromise.prototype.resolve = function () {
if (this.state === this.states.PENDING) {
this.state = this.states.RESOLVE
}
}
到目前為止,我們可以通過 executor 內(nèi)部調(diào)用 reject 或者 resolve 修改某個(gè) Promise 的狀態(tài)了.
在回到我們使用 ES6 的 Promise 代碼中.
new Promise ((resolve,reject) => {
resolve(someData) // or reject(someError)
})
可以知道 reslove 和 reject 是可以接受參數(shù)的.
- 對于 resolve 來說,參數(shù)就是 executor 執(zhí)行成功返回的的值.
- 對于 reject 來說,參數(shù)就是 executor 執(zhí)行失敗返回的值.
6. 給 reject 和 resolve 來設(shè)置一個(gè)形參
// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
if (this.state === this.states.PENDING) {
this.state = this.states.REJECT
}
}
// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
if (this.state === this.states.PENDING) {
this.state = this.states.RESOLVE
}
}
7.我們通過 then 傳遞了兩個(gè)函數(shù),第一個(gè)是成功的回調(diào). 第二個(gè)是失敗的回調(diào).
MyPromise.prototype.then = function (onResolve,onReject) {
this.onResloveCallback = onResolve
this.onRejectCallback = onReject
}
在回到 ES6 的 Promise 調(diào)用代碼中.
let p = new Promise((resolve,reject) => {
setTimeout(()=>{
resolve(data形參) // reject(失敗形參)
},1000)
})
p.then(data=>{
data === data 形參
})
8.resolve的形參傳遞給了then的第一個(gè)回調(diào)函數(shù)的參數(shù),error傳遞給了then的第二個(gè)回調(diào)函數(shù)的參數(shù)
// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
if (this.state === this.states.PENDING) {
this.state = this.states.REJECT
this.error = error
}
}
// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
if (this.state === this.states.PENDING) {
this.state = this.states.RESOLVE
this.data = data
}
}
9. 狀態(tài)改變完成之后,就需要調(diào)用從then函數(shù)接受過來的兩個(gè)函數(shù)參數(shù)了(reject&resolve)
// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
if (this.state === this.states.PENDING) {
this.state = this.states.REJECT // 該狀態(tài)
this.error = error // 記錄值
this.onRejectCallback(this.error) // 調(diào)用回調(diào)
}
}
// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
if (this.state === this.states.PENDING) {
this.state = this.states.RESOLVE // 改狀態(tài)
this.data = data // 記錄值
this.onResolveCallback(data) // 調(diào)用回調(diào)
}
}
完整的代碼
function MyPromise (executor) {
this.state = this.states.PENDING
this.value = null // 用于記錄executor內(nèi)部的reject or resolve 傳遞回來的值.
this.onRejectCallback = null
this.onResolveCallback = null
executor(this.resolve.bind(this),this.reject.bind(this))
}
MyPromise.prototype.states = {
PENDING: 'PENDING',
RESOLVE: 'RESOLVE',
REJECT: 'REJECT'
}
// reject 給 error 形參
MyPromise.prototype.reject = function (error) {
if (this.state === this.states.PENDING) {
this.state = this.states.REJECT // 該狀態(tài)
this.error = error // 記錄值
this.onRejectCallback(this.error) // 調(diào)用回調(diào)
}
}
// resolve 給 data 形參
MyPromise.prototype.resolve = function (data) {
if (this.state === this.states.PENDING) {
this.state = this.states.RESOLVE // 改狀態(tài)
this.data = data // 記錄值
this.onResolveCallback(data) // 調(diào)用回調(diào)
}
}
step 03 . 測試MyPromise 的 resolve 和 reject
測試調(diào)用自定義的promise ---> pending --> resolve
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })
正確輸出.

測試調(diào)用自定義的promise ---> pending --> reject
new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })
錯(cuò)誤輸出.

第一階段手寫 Promise 達(dá)成目標(biāo):
- 狀態(tài)改變 [?]
- 正確的調(diào)用then的函數(shù)接受的兩個(gè)回調(diào)函數(shù).[?]
補(bǔ)充一點(diǎn):
如果把 reject 和 resolve 寫在 prototype 上,然后直接通過 this.reject/resolve 傳遞會導(dǎo)致 this 指向的錯(cuò)誤(obj.method當(dāng)函數(shù)傳遞,只能傳 method, 不會傳 obj,真正的純函數(shù)傳遞).所以,上述代碼一定要寫成.
executor(this.resolve.bind(this),this.reject.bind(this))