什麼是Promise?本代碼用定外賣來舉例子,讓你明白。
// 定外賣就是一個Promise,Promist的意思就是承諾
// 咱們定完外賣,飯不會當即到咱們手中
// 這時候咱們和商家就要達成一個承諾
// 在將來,無論飯是作好了仍是燒糊了,都會給咱們一個答覆
function orderFood(){
// Promise 接受兩個參數
// resolve: 異步事件成功時調用(菜燒好了)
// reject: 異步事件失敗時調用(菜燒糊了)
return new Promise((resolve, reject) => {
let result
console.log('in promise')
setTimeout(()=>{
// 商家廚房作飯,模擬機率事件
result = Math.random() > 0.5 ? '菜燒好了' : '菜燒糊了'
// 下面商家給出承諾,無論燒沒燒好,都會告訴你
if (result == '菜燒好了'){
// 商家給出了反饋
resolve('咱們的外賣正在給您派送了')
}else{
reject('很差意思,咱們菜燒糊了,您再等一會')
}
}, 5000)
})
}
// 你在家上餓了麼定外賣
// 有一半的機率會把你的飯燒糊了
// 好在有承諾,他仍是會告訴你
// 菜燒好執行,返回'咱們的外賣正在給您派送了'
console.log('before order')
orderFood().then(res => console.log(res))
// 菜燒糊了執行,返回'很差意思,咱們菜燒糊了,您再等一會'
.catch(res => console.log(res))
console.log('after order')
爲了讓你們更容易理解,咱們從一個場景開始講解,讓你們一步一步跟着思路思考,相信你必定會更容易看懂。設計模式
考慮下面一種獲取用戶id的請求處理數組
//例1
function getUserId() {
return new Promise(function(resolve) {
//異步請求
http.get(url, function(results) {
resolve(results.id)
})
})
}
getUserId().then(function(id) {
//一些處理
})
getUserId
方法返回一個promise
,能夠經過它的then
方法註冊(注意註冊
這個詞)在promise
異步操做成功時執行的回調。這種執行方式,使得異步調用變得十分順手。promise
那麼相似這種功能的Promise
怎麼實現呢?其實按照上面一句話,實現一個最基礎的雛形仍是很easy的。dom
function Promise(fn) {
var value = null,
callbacks = []; //callbacks爲數組,由於可能同時有不少個回調
this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
};
function resolve(value) {
callbacks.forEach(function (callback) {
callback(value);
});
}
fn(resolve);
}
上述代碼很簡單,大體的邏輯是這樣的:異步
then
方法,將想要在Promise
異步操做成功時執行的回調放入callbacks
隊列,其實也就是註冊回調函數,能夠向觀察者模式方向思考;Promise
實例時傳入的函數會被賦予一個函數類型的參數,即resolve
,它接收一個參數value,表明異步操做返回的結果,當一步操做執行成功後,用戶會調用resolve
方法,這時候其實真正執行的操做是將callbacks
隊列中的回調一一執行;
能夠結合例1
中的代碼來看,首先new Promise
時,傳給promise
的函數發送異步請求,接着調用promise
對象的then
屬性,註冊請求成功的回調函數,而後當異步請求發送成功時,調用resolve(results.id)
方法, 該方法執行then
方法註冊的回調數組。函數
相信仔細的人應該能夠看出來,then
方法應該可以鏈式調用,可是上面的最基礎簡單的版本顯然沒法支持鏈式調用。想讓then
方法支持鏈式調用,其實也是很簡單的:this
this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
return this;
};
see?只要簡單一句話就能夠實現相似下面的鏈式調用:url
// 例2
getUserId().then(function (id) {
// 一些處理
}).then(function (id) {
// 一些處理
});
細心的同窗應該發現,上述代碼可能還存在一個問題:若是在then
方法註冊回調以前,resolve
函數就執行了,怎麼辦?好比promise
內部的函數是同步函數:prototype
// 例3
function getUserId() {
return new Promise(function (resolve) {
resolve(9876);
});
}
getUserId().then(function (id) {
// 一些處理
});
這顯然是不容許的,Promises/A+
規範明確要求回調須要經過異步方式執行,用以保證一致可靠的執行順序。所以咱們要加入一些處理,保證在resolve
執行以前,then
方法已經註冊完全部的回調。咱們能夠這樣改造下resolve
函數:設計
function resolve(value) {
setTimeout(function() {
callbacks.forEach(function (callback) {
callback(value);
});
}, 0)
}
上述代碼的思路也很簡單,就是經過setTimeout
機制,將resolve
中執行回調的邏輯放置到JS
任務隊列末尾,以保證在resolve
執行時,then
方法的回調函數已經註冊完成.
剛開始看promise源碼的時候總不能很好的理解then和resolve函數的運行機理,可是若是你靜下心來,反過來根據執行promise時的邏輯來推演,就不難理解了。這裏必定要注意的點是:promise裏面的then函數僅僅是註冊了後續須要執行的代碼,真正的執行是在resolve方法裏面執行的,理清了這層,再來分析源碼會省力的多。
如今回顧下Promise的實現過程,其主要使用了設計模式中的觀察者模式: