Promise超神超簡單總結:
之前總感覺promise很神祕,今天仔細讀了阮一峯大神的es6,也查詢了下相關的promise原理,終於明白了promise怎麼這麼6.
簡介:
Promise 對象用於延遲(deferred) 計算和異步(asynchronous )計算.一個Promise對象代表着一個還未完成,但預期將來會完成的操作。Promise 對象是一個返回值的代理,這個返回值在promise對象創建時未必已知。它允許你爲異步操作的成功或失敗指定處理方法。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個包含了原返回值的 promise 對象來替代原返回值。
解決了什麼問題及怎麼使用:
//一個簡單的示例 執行一個動畫A,執行完之後再去執行另一個動畫B
setTimeout(function(){
//A動畫
console.log('A');
setTimeout(function() {
//B動畫
console.log('B');
},300)
},300);
//這裏只有兩個動畫,如果有更多呢,就會看到一堆函數縮進
不難想象,如果依次有很多個動畫,就會出現多重嵌套。代碼不是縱向發展,而是橫向發展,很快就會亂成一團,無法管理。因爲多個異步操作形成了強耦合,只要有一個操作需要修改,它的上層回調函數和下層回調函數,可能都要跟着修改。這種情況就稱爲回調函數地獄“(callback hell)。
Promise 對象就是爲了解決這個問題而提出的。它不是新的語法功能,而是一種新的寫法,允許將回調函數的嵌套,改成鏈式調用。
Promise 的最大問題是代碼冗餘,原來的任務被 Promise 包裝了一下,不管什麼操作,一眼看去都是一堆then,原來的語義變得很不清楚。
(1)瀏覽器實現方式 可以在支持Promise的版本上運行
var p = new Promise(function(resolve, reject){
setTimeout(function(){
//A動畫
console.log('A');
resolve();
},300);
});
p.then(function(){
setTimeout(function() {
//B動畫
console.log('B');
},300);
});
(2)另一種寫法(jQuery版本)
var deferred = $.Deferred();
setTimeout(function(){
//A動畫
console.log('A');
deferred.resolve();
},300);
deferred.done(function() {
setTimeout(function() {
//B動畫
console.log('B');
},300)
});
promise會讓代碼變得更容易維護,像寫同步代碼一樣寫異步代碼。
promise原理
其實,promise就是三個狀態。利用觀察者模式,只需要通過特定書寫方式註冊對應狀態的事件處理函數,然後更新狀態,調用註冊過的處理函數即可。
這個特定方式就是then,done,fail,always…等方法,更新狀態就是resolve、reject方法。
下面簡單實現:
/**
/**
* [3種狀態]
* @type {String}
*/
var PENDING = "pending";
var RESOLVED = "resolved";
var REJECTED = "rejected";
/**
* [Promise類實現]
* 構造函數傳入一個fn,有兩個參數,resolve:成功回調; reject:失敗回調;
* state: 狀態存儲
* doneList: 成功處理函數列表
* failList: 失敗處理函數列表
* done: 註冊成功處理函數
* fail: 註冊失敗處理函數
* then: 同時註冊成功和失敗處理函數
* always: 一個處理註冊到成功和失敗,都會調用
* resolve: 更新state爲:RESOLVED,並且執行成功處理隊列
* reject: 更新state爲:REJECTED,並且執行失敗處理隊列
*/
var Promise2 = (function() {
var noop = function() {}
function Promise(fn) {
this.state = PENDING;
this.doneList = [];
this.failList = [];
this.fn = fn;
this.fn(this.resolve.bind(this), this.reject.bind(this)) //立即執行構造函數的第一個參數
}
var p = {
done: function(cb) {
if (typeof cb == "function")
this.doneList.push(cb)
return this;
},
fail: function(cb) {
if (typeof cb == "function")
this.failList.push(cb);
return this;
},
then: function(success, fail) {
this.done(success || noop).fail(fail || noop)
return this;
},
always: function(cb) {
this.done(cb).fail(cb)
return this;
},
resolve: function() {
this.state = RESOLVED;
var lists = this.doneList;
for (var i = 0; i < lists.length;) {
lists[i].apply(this, arguments);
lists.shift();
}
return this;
},
reject: function() {
this.state = REJECTED;
var lists = this.failList;
for (var i = 0; i < lists.length;) {
lists[0].apply(this, arguments);
lists.shift();
}
return this;
}
}
for (var k in p) {
Promise.prototype[k] = p[k]
}
return Promise;
})();
//測試代碼
var p1 = new Promise2(function(resolve,reject){
setTimeout(resolve,2000);
console.log('p1',new Date);
});
p1.then(function(){
console.log("end",new Date);
}).always(function(){
console.log('always',new Date);
});
利用觀察者模式,只需要通過特定書寫方式,註冊對應狀態的事件處理函數,然後更新狀態,調用註冊過的處理函數即可。
解釋:當我們調用new Promise(fn)時,就會立即執行第一個參數fn。上面案例中,先將then(done,fail)對應的回調函數分別加入到doneList或者failList中,always中對應的參數同時加到doneList和failList中,當異步代碼執行完畢,就調用resolve方法,此時改變promise的狀態爲resolved,並執行doneList裏面的回調函數。如果執行失敗,則調用fail方法,此時改變promise的狀態爲rejected,並執行failList裏面的回調函數。
4、generator的一個小應用
function * gen(m,n){
for(let i=m;i<n;i++){
yield i;
}
}
console.log(...gen(1,10));
//結果: 1 2 3 4 5 6 7 8 9
參考鏈接:
Promise: http://imweb.io/topic/565af932bb6a753a136242b0