在介紹Promise之前,首先我們舉一個栗子,看下面代碼
function success (message) {console.log(message)}function fail (message) {console.log(message)}function readFile (success, fail) {console.log('文件讀取啟動了...')setTimeout(() => {if ((+ new Date()) %2 === 0) {success('success:文件讀取成功了')} else {fail('fail:文件讀取失敗了')}}, 1000)}readFile(success, fail) // 執行 readFile 函數console.log('end...')
輸出結果:
【分析】
- 這里我們定義了一個 readFile 函數,里面用一個 setTimeout 定時器模擬讀取文件得異步操作
- 而后定時器里分情況調用 success 或 fail 函數 - 首先我們需要注意得是,在這里如果我們需要啟動定時器,必須執行 readFile() ,同時也必須把回調函數一起傳進去
- 也就是說,如果 readFile 函數內部已經開始執行得時候 - 如果要有結果,此時必定有 success 和 fail 函數 - 簡單概括的話,就是在有結果輸出的情況下, readFile 函數內部元素壓入調用棧開始執行時, success 和 fail 回調函數 必定存在 --- 即已經定義好了
- 至于為什么會用這個栗子開頭,請接著往下看....
初識 Promise
舉個栗子,看下面的代碼:
let file = new Promise ((resolve, reject) => {console.log('promise 讀取文件開始了...')setTimeout(() => {if ((+ new Date()) %2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})setTimeout(() => {// 3秒之后開始傳入回調函數file.then((res) => {console.log(res)}).catch((err) => {console.log(err)})}, 3000)console.log('end...')
輸出結果:
【分析】
- 在第一行代碼,我們 new 了一個 Promise 對象 - 里面用了一個 1s 定時器 模擬 文件讀取 的異步操作
- 看輸出結果,第一句輸出的是 Promise 內部的第一行代碼,也就是說,new Promise 對象時,內部已經開始執行了,
- 1s之后,分情況調用 resolve 或 reject 函數 - 我們注意到,此時我們還沒有傳入回調函數,resolve 和 reject 只是我們 new Promise 時定義的形式參數
- 3s 后,我們開始傳入回調函數,這時,我們在 3s 后傳入的回調函數執行了并且出現了結果
【思考】
我們回想一開始舉的例子
readFile 函數執行的時候,此時回調函數已經定義好了,并且作為參數傳入了 readFile 函數 new Promise 的時候,內部已經開始執行,此時我們并未傳入回調函數 3s 之后,我們才傳入回調函數,神奇的是最后有結果輸出【提示】
這樣一對比,你是不是發現了什么
Promise 一 創建對象 內部開始執行,此時我們沒有傳入回調函數 未來的某段時間里,我們傳入回調函數,回調函數可能會執行并且可以取到結果【分析總結】
- 在 Promise 內部,我們 new 的時候傳入的函數應該立即執行
- 因為在未來的某段時間傳入回調函數可以取到結果,所以 Promise 內部應該有一個變量將 結果 緩存 起來了
手動實現 Promise
- 我們通過 new 創建 Promise 對象,所以 Promise 應該是一個函數,同時傳入一個函數作為參數,該函數立即執行
- Promise 有 3 種狀態:pending、fulfilled、rejected(等待中、已成功、已失敗),并且狀態只能pending -> fulfilled (等待中 -> 已成功)或 pending -> rejected(等待中 -> 已失敗),改變之后狀態不能再次變化
- 內部有變量存儲 失敗 或 成功 的結果,這里定義為存儲變量為 data
- 同時 Promise 可以鏈式調用傳入回調函數,因此還存在變量保存回調函數集合,這里定義 保存成功的變量 onResolvedCallback,保存失敗的變量 onRejectedCallback
let myPromise = (function () {// 定義變量保存狀態的名字const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 成功function Promise(executor) {this.status = PENDING // 定義變量保存 狀態,初始時為 pendingthis.data = undefined // 定義變量保存 結果this.onResolvedCallback = [] // resolve時的回調函數集合this.onRejectedCallback = [] // reject時的回調函數集合// 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來,并且在出錯后以catch捕獲到的值reject掉這個Promisetry {// 因為new Promise對象時傳入一個函數,該函數立即執行executor(resolve, reject) // 執行executor} catch (e) {reject(e)}}return Promise})()let p = new myPromise(function(resolve, reject) {setTimeout(() => {if ((+ new Date()) %2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})
輸出結果
- 因為我們在內部調用了 resolve 和 reject , 這兩個變量在函數內部我們沒有定義,所以出錯。那我們在內部定義的這兩個函數需要處理什么呢?
- resolve 應該取狀態變為 成功 的 結果,reject 應該取狀態變為 失敗 的結果。
繼續完善代碼
let myPromise = (function () {// 定義變量保存狀態的名字const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 成功function Promise(executor) {this.status = PENDING // 定義變量保存 狀態,初始時為 pendingthis.data = undefined // 定義變量保存 結果this.onResolvedCallback = [] // resolve時的回調函數集合this.onRejectedCallback = [] // reject時的回調函數集合function resolve(result) { if (this.status === PENDING) { // 狀態只能是 pending, 才可以執行下面的代碼this.status = FULFILLED // 因為調用 resolve,所以是成功的,所以狀態變為 fulfilledthis.data = result // 保存 成功 的結果// 開始給 誰想要當前 成功的 結果,就執行他們的函數,并且將 成功 的結果傳給他們this.onResolvedCallback.forEach(cb => cb(this.data))}}function reject(reason) {// 同上if (this.status === PENDING) {this.status = REJECTEDthis.data = reasonthis.onRejectedCallback.forEach(cb => cb(this.data))}}// 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來,并且在出錯后以catch捕獲到的值reject掉這個Promisetry {// 因為new Promise對象時傳入一個函數,該函數立即執行executor(resolve, reject) // 執行executor} catch (e) {reject(e)}}return Promise})()let p = new myPromise(function (resolve, reject) {setTimeout(() => {if ((+ new Date()) % 2 === 0) {resolve('success:文件讀取成功了')} else {reject('fail:文件讀取失敗了')}}, 1000)})
到這一步,我們剛才分析的內容大部分完成了 如果我們 MDN 上搜索 Promise
MDN上Promise介紹
- 我們發現 then、catch、finally 是 Promise 類的實例方法
- all、race、reject 、resolve 是 Promise 類的靜態方法
- 所以我們根據分析,可以得出如下的 結構
Promise.prototype.then = function() {}
Promise.prototype.catch = function() {}
Promise.all = function() {}
Promise.race = function() {}
Promise.reject = function() {}
Promise.resolve = function() {}
Promise.then 方法實現
- Promise.then 接受兩個參數,一個為 成功狀態 的回調函數,一個為 失敗狀態 的回調函數
- 同時我們可以不傳回調函數,這時直接取 上一個處理的結果,不做處理,直接傳遞到 下一個
講解
- 首先,then 可以鏈式調用,所以每次都需要返回一個 Promise 對象
Promise.prototype.then = function (onFulfilled, onRejected) {return new Promise(function (resolve, reject) {}}
- 我們通過前面的分析,已經知道 then 里面的回調函數可以 在不同時間段給出,此時,當前這個 promise 可能出現 3 種狀態 (等待中、已成功、已失敗),所以分 3 種情況 處理
Promise.prototype.then = function (onFulfilled, onRejected) {return new Promise(function (resolve, reject) {if (self.status == FULFILLED) { // 已成功}if (self.status == REJECTED) { // 已失敗}if (self.status === PENDING) { // 等待中}})}
已成功
- 因為 狀態已經變成了 已成功,所以存儲了 成功的結果,我們將 成功的結果 作為參數傳遞給 onFulfilled 回調函數并執行
- 因為 返回的 promise 的狀態由 onFulfilled 回調函數執行的結果決定
- 所以我們開始分析 onFulfilled 可能有什么樣的結果
如果拋出異常,return 的 Promise 就會失敗,結果 就是 error如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果- 上面 3 種 結果是什么意思呢?我們以下面的栗子來說明
var p1 = new Promise()// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 error
p1.then(() => {
// 就是有人會這么直接拋出異常,所以 p1 return 的 Promise 狀態應該是 已失敗throw 233
})// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值
p1.then(() => {
// 直接 return 一個 值,因為沒有拋出異常,所以不是 已失敗狀態,同時 因為有了結果,所以不是 等待中,最后return 的 Promise 只能是 已成功 狀態,return 1433
})
// 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果
p1.then(() => {
// 因為 返回 了 一個 Promise,所以當前狀態無法確定,只能通過 返回的 這個 Promise 的最后狀態來確定return new Promise(() => {})
})
- 根據 分析,我們初步實現 已成功 的 內容,因為要用到 Promise 的 存儲的結果 data,所以我們定義一個變量self= this
Promise.prototype.then = function (onFulfilled, onRejected) { let self = thisreturn new Promise(function (resolve, reject) {if (self.status == FULFILLED) {// 已成功setTimeout(() => {try {// 獲取 回調函數執行的結果var _result = onFulfilled(self.data)if (_result instanceof Promise) {// 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值resolve(_result)}} catch (e) {// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 errorreject(e)}})}if (self.status == REJECTED) {// 已失敗}if (self.status === PENDING) {// 等待中}})}
問題:為什么 then回調函數必須設置為 setTimeout 的回調?
setTimeout(() => { // 保證返回的 Promise 是異步try {var _result = onFulfilled(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})
- 保證返回的 Promise 是異步
- (這個部分,下次更新)
已失敗
- 分析的思路和結果與上面的差不多,不做過多分析
- 需要注意的是此時的 _result = onRejected(self.data) ,調用失敗的回調函數
等待中
- 因為當前 Promise 的狀態是 進行中, 所以我們無法得知最后的狀態是
- 我們得等到 Promise 的 最后狀態確定了 ,才能 知道 是調用 onFulfilled 還是 onRejected
- 這時候我們通過發布訂閱模式,將當前傳入的回調函數都保存起來,等到有了結果再進行通知,并把最后狀態的結果傳給回調函數
Promise.prototype.then = function (onFulfilled, onRejected) {let self = thisreturn new Promise(function (resolve, reject) {if (self.status == FULFILLED) {// 已成功setTimeout(() => {try {// 獲取 回調函數執行的結果var _result = onFulfilled(self.data)if (_result instanceof Promise) {// 3. 如果回調函數返回是 Promise,return 的 Promise 結果就是這個 Promise 的結果_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {// 2. 如果回調函數返回不是 Promise,return 的 Promise 就會成功,value 就是返回的值resolve(_result)}} catch (e) {// 1. 如果拋出異常,return 的 Promise 就會失敗,結果 就是 errorreject(e)}})}if (self.status == REJECTED) { // 已失敗setTimeout(() => {try {var _result = onRejected(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})}if (self.status === PENDING) { // 等待中self.onResolvedCallback.push(function () {setTimeout(() => {try {var _result = onFulfilled(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})})self.onRejectedCallback.push(function () {setTimeout(() => {try {var _result = onRejected(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}})})}})}
- 我們發現有 四個 地方的內容我們重復寫了 四遍,所以提取出來就變成了
Promise.prototype.then = function (onFulfilled, onRejected) {let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}
透傳
- 由于我們調用 .then 方法時,可以一個回調函數都不傳,此時應當不對最后狀態的結果進行處理,直接傳遞到下一個 Promise
- 請始終記得,只要沒有手動對狀態確定的 Promise 的結果進行 過處理,Promise 的狀態就不會再改變
Promise.prototype.then = function (onFulfilled, onRejected) {onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : (value) => valueonRejected = typeof onRejected == 'function' ? onRejected : (reason) => { throw reason }let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}
Promise.catch 方法實現
- catch 方法,傳入一個失敗的回調函數,因為需要獲取 失敗的結果,所以我們直接調用當前 Promise 的 then 方法來獲取 失敗的結果
- 同時調用了 .catch 之后,還是返回一個 Promise 對象,并且 下一個 Promise 的狀態由 .catch 的回調函數 返回值確定
- 在這里人們會存在的一個誤解是:調用 .catch 之后,以后的 Promise 狀態都是 已失敗,這是錯誤的
- 你需要時刻記得的是:只要你沒有用回調函數處理過 已失敗 的結果,之后的 Promise 狀態就是 已失敗,只要你 用回調函數處理了 已失敗的結果,之后的 Promise 狀態就根據你 回調函數的 結果來 確定。
Promise.prototype.catch = function (onRejected) {return this.then(undefined, onRejected)}
Promise.all 方法實現
- Promise.all 方法傳入一個數組,當數組中有一個 結果是 已失敗 時,最后結果就是 已失敗,只有當數組中的 所有 結果都是 成功時,該方法才會被確定為 已成功 的狀態
- 已成功 的 結果是一個數組
- 需要注意的是,已成功 的 結果數組,和 Promise.all 傳入的數組是相對應得
- 也就是說,傳入的第 2 個 是 已成功 數組 的 第 2 個 結果
- 這里我們可以借鑒 我們常遇到的 保證多個ajax 請求都返回了結果 的解決 辦法。
- 我們通過一個變量(successNum)來保存 已成功 的數量,如果最后該變量的值 和 傳入的數組 長度 一樣,那就說明 傳入的 數組 所有結果都是 已成功
- 遇到 一個執行結果是 已失敗,直接返回 已失敗 的狀態
【分析】傳入的數組可能的情況
- [1, '233', new Promise()]
- 一是 字符串或數字
- 二是 Promise 對象
Promise.all = function (promises) {let len = promises.lengthlet successNum = 0let _result = []return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {// 針對 Promise 對象if (p instanceof Promise) {p.then((res) => {successNum++_result[i] = resif (successNum === len) {resolve(_result)}},(err) => {reject(err)})} else {// 非 Promise 對象successNum++_result[i] = pif (successNum === len) {resolve(_result)}}} catch (err) {reject(err)}})})
}
Promise.race 方法實現
- Promise.race , 傳入的是一個數組,誰先有結果,返回的 Promise 的狀態就是該結果的 狀態
- 該方法和 Promise.all 相比,不需要變量 保存 誰已經執行了,只需要遇到哪個執行完成了,直接返回和其結果一樣的狀態
Promise.race = function (promises) {return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(p)}} catch (err) {reject(err)}})})
}
Promise.reject 方法實現
- 該方法返回一個 已失敗 狀態 的 Promise
- 直接通過新建一個 Promise 對象,只傳入第二個參數,返回一個 已失敗 的 Promise
Promise.reject = function (rejected) {return new Promise((undefined, reject) => {reject(rejected)})
}
Promise.resolve 方法實現
- 該方法返回一個 已成功 狀態的 Promise
- 我們同樣通過直接返回一個 Promise 對象來 獲取 返回成功的 狀態
- 這里需要注意的是:因為是返回 一個 已成功 的狀態,但是我們并不能保證 用戶傳入的 結果最后就是 已成功 的狀態
- 該方法和 Promise.reject 不同
- 用戶如果傳入了一個 Promise 對象 作為參數,那我們應該 取 傳入的參數結果和狀態作為 當前 Promise 最后返回的結果和狀態
Promise.resolve = function (result) {return new Promise((resolve, reject) => {try {// 如果傳入的是一個 Promise 類型的變量,取它的狀態和結果作為 Promise.resolve 的狀態和結果if (result instanceof Promise) {result.then((res) => {resolve(res)},(err) => {reject(err)})}resolve(result)} catch (e) {reject(e)}})
}
結果
const _Promise = (function () {const PENDING = 'pending' //初始狀態const FULFILLED = 'fulfilled' // 成功狀態const REJECTED = 'rejected' // 失敗狀態function Promise(executor) {var self = thisself.status = PENDINGself.data = undefinedself.onResolvedCallback = []self.onRejectedCallback = []function resolve(result) {if (self.status === PENDING) {self.status = FULFILLEDself.data = resultself.onResolvedCallback.forEach((cb) => cb(self.data))}}function reject(reason) {if (self.status === PENDING) {self.status = REJECTEDself.data = reasonself.onRejectedCallback.forEach((cb) => cb(self.data))}}try {executor(resolve, reject)} catch (e) {reject(e)}}Promise.prototype.then = function (onFulfilled, onRejected) {onFulfilled =typeof onFulfilled == 'function' ? onFulfilled : (value) => valueonRejected =typeof onRejected == 'function'? onRejected: (reason) => {throw reason}let self = thisreturn new Promise(function (resolve, reject) {// 根據指定回調函數處理,根據執行結果,改變 return 的 promise 的狀態function handle(callback) {try {var _result = callback(self.data)if (_result instanceof Promise) {_result.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(_result)}} catch (e) {reject(e)}}if (self.status == FULFILLED) {setTimeout(() => {handle(onFulfilled)})}if (self.status == REJECTED) {// 如果當前是 resolved 狀態,異步執行 onRejected 并改變 return 的 promise 狀態setTimeout(() => {handle(onRejected)})}if (self.status === PENDING) {self.onResolvedCallback.push(function () {setTimeout(() => {handle(onFulfilled)})})self.onRejectedCallback.push(function () {setTimeout(() => {handle(onRejected)})})}})}Promise.prototype.catch = function (onRejected) {return this.then(undefined, onRejected)}Promise.all = function (promises) {let len = promises.lengthlet successNum = 0let _result = []return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {successNum++_result[i] = resif (successNum === len) {resolve(_result)}},(err) => {reject(err)})} else {successNum++_result[i] = pif (successNum === len) {resolve(_result)}}} catch (err) {reject(err)}})})}Promise.race = function (promises) {return new Promise((resolve, reject) => {promises.forEach((p, i) => {try {if (p instanceof Promise) {p.then((res) => {resolve(res)},(err) => {reject(err)})} else {resolve(p)}} catch (err) {reject(err)}})})}Promise.reject = function (rejected) {return new Promise((undefined, reject) => {reject(rejected)})}Promise.resolve = function (result) {return new Promise((resolve, reject) => {try {if (result instanceof Promise) {result.then((res) => {resolve(res)},(err) => {reject(err)})}resolve(result)} catch (e) {reject(e)}})}return Promise})()let p1 = new _Promise((resolve, reject) => {setTimeout(() => {resolve('p1 ---> resolve')}, 1000)})let p2 = new _Promise((resolve, reject) => {setTimeout(() => {resolve('p2 ---> resolve')}, 500)})let p3 = new _Promise((resolve, reject) => {setTimeout(() => {reject('p3 ---> reject')}, 1500)})_Promise.all([p1, p2, 120, 'hello', p3]).then((res) => {console.log('resolve', res)}).then(() => {},(err) => {console.log('err', err)throw ''}).then((res) => {console.log('res', res)},(err) => {console.log('err', err)})
【輸出結果】
err p3 ---> reject
err
總結
以上是生活随笔為你收集整理的定时器和promise_分析 Promise 内部实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。