在面試的時候,常常會有面試官讓你實現一個Promise,若是參照A+規範來實現的話,可能面到天黑都結束不了。前端
說到Promise,咱們首先想到的最核心的功能就是異步鏈式調用,本篇文章就帶你用20行代碼實現一個能夠異步鏈式調用的Promise。git
這個Promise的實現不考慮任何異常狀況,只考慮代碼最簡短,從而便於讀者理解核心的異步鏈式調用原理。github
先給代碼吧,真就20行。面試
function Promise(excutor) {
var self = this
self.onResolvedCallback = []
function resolve(value) {
setTimeout(() => {
self.data = value
self.onResolvedCallback.forEach(callback => callback(value))
})
}
excutor(resolve.bind(self))
}
Promise.prototype.then = function(onResolved) {
var self = this
return new Promise(resolve => {
self.onResolvedCallback.push(function() {
var result = onResolved(self.data)
if (result instanceof Promise) {
result.then(resolve)
} else {
resolve(result)
}
})
})
}
複製代碼
new Promise(resolve => {
setTimeout(() => {
resolve(1)
}, 500)
})
.then(res => {
console.log(res)
return new Promise(resolve => {
setTimeout(() => {
resolve(2)
}, 500)
})
})
.then(console.log)
複製代碼
本文將圍繞這個最核心的案例來說,這段代碼的表現以下:數組
首先來實現Promise構造函數promise
function Promise(excutor) {
var self = this
self.onResolvedCallback = [] // Promise resolve時的回調函數集
// 傳遞給Promise處理函數的resolve
// 這裏直接往實例上掛個data
// 而後把onResolvedCallback數組裏的函數依次執行一遍就能夠
function resolve(value) {
// 注意promise的then函數須要異步執行
setTimeout(() => {
self.data = value
self.onResolvedCallback.forEach(callback => callback(value))
})
}
// 執行用戶傳入的函數
excutor(resolve.bind(self))
}
複製代碼
好,寫到這裏先回過頭來看案例frontend
const excutor = resolve => {
setTimeout(() => {
resolve(1)
}, 500)
}
new Promise(excutor)
複製代碼
分開來看,excutor就是用戶傳的函數,這個函數內部調用了resolve函數後,就會把promise實例上的onResolvedCallback執行一遍。異步
到此爲止咱們還不知道onResolvedCallback這個數組裏的函數是從哪裏來的,接着往下看。函數
這裏是最重要的then實現,鏈式調用全靠它:ui
Promise.prototype.then = function(onResolved) {
// 保存上下文,哪一個promise調用的then,就指向哪一個promise。
var self = this
// 必定要返回一個新的promise
// promise2
return new Promise(resolve => {
self.onResolvedCallback.push(function() {
var result = onResolved(self.data)
if (result instanceof Promise) {
// resolve的權力被交給了user promise
result.then(resolve)
} else {
resolve(result)
}
})
})
}
複製代碼
再回到案例裏
var excutor = resolve => {
setTimeout(() => {
resolve(1)
}, 500)
}
var promise1 = new Promise(excutor)
promise1.then(res => {
console.log(res)
// user promise
return new Promise(resolve => {
setTimeout(() => {
resolve(2)
}, 500)
})
})
複製代碼
注意這裏的命名:
咱們把Promise構造函數返回的實例叫作promise1
,
在then的實現中,咱們構造了一個新的promise返回,叫它promise2
在用戶調用then方法的時候,用戶手動構造了一個promise用來作異步的操做,叫它user promise
那麼在then的實現中,self其實就指向promise1
而promise2
的excutor中,馬上執行了一個函數,它往promise1
的onResolvedCallback
數組中push了一個函數,
那麼重點看這個push的函數,注意,這個函數在promise1被resolve了之後纔會執行。
self.onResolvedCallback.push(function() {
// onResolved就對應then傳入的函數
var result = onResolved(self.data)
// 例子中的狀況 返回了一個promise3
if (result instanceof Promise) {
// 那麼直接把promise2的resolve決定權交給了user promise
result.then(resolve)
} else {
resolve(result)
}
})
複製代碼
若是用戶傳入給then的onResolved方法返回的是個promise,那麼這個user promise
裏拿到的參數resolve,其實就指向了內部promise2
的resolve,
因此這就能夠作到:user promise
被resolve之後,then2
函數纔會繼續執行,
new Promise(resolve => {
setTimeout(() => {
// resolve1
resolve(1)
}, 500)
})
// then1
.then(res => {
console.log(res)
// user promise
return new Promise(resolve => {
setTimeout(() => {
// resolve2
resolve(2)
}, 500)
})
})
// then2
.then(console.log)
複製代碼
then1
其實進入了promise1
的回調數組裏,因此resolve1
執行完畢後,then1
纔會執行
then2
其實進入了promise2
的回調數組裏,又由於咱們剛剛知道,resolve2
正是promise2
的resolve方法,
因此resolve2
執行完畢後, then2
纔會執行,這就實現了異步的鏈式調用。
一個核心的要點:
then1
函數是個同步函數,返回一個普通的值。 then1
裏傳入的函數,實際上是被放到promise1
的回調數組裏,// promise1
new Promise(resolve => {
setTimeout(resolve, 1000)
})
// then1 這裏傳入的函數 會被放到調用者promise的回調數組中
.then(res => {
console.log(res)
})
複製代碼
這樣的話,1秒後,promise1
被resolve了,是否是then1
裏的函數就被執行了呢~
// 調用then的promise
new Promise(resolve => {
setTimeout(resolve, 1000)
})
// then2
.then(res => {
// user promise
return new Promise(resolve => {
setTimeout(resolve, 1000)
})
})
// then3
.then(res => {
console.log(res)
})
複製代碼
then2
會返回promise2
(注意不是user promise
,而是源碼內部返回的那個promise2
),
then3
傳入的函數會被放到promise2
的回調數組裏。
因爲then2
中用戶本身返回了一個user promise
,
因此promise2
的resolve權力會被交給user promise
,
在1秒後,user promise
被resolve了,那麼表明着promise2
被reoslve了,那麼在promise2
的回調數組裏會找到then3
傳入的回調函數
它就被完美的執行了。
以上代碼所有整理在了 Github倉庫 中
本文只是簡單實現一個能夠異步鏈式調用的promise,而真正的promise比它複雜不少不少,涉及到各類異常狀況、邊界狀況的處理。
promise A+規範仍是值得每個合格的前端開發去閱讀的。
但願這篇文章能夠對你有所幫助!