繼續填坑
模式
考慮下面的代碼:
function fn(x) { //do something return new Promise(function(resolve, reject) { //調用resolve(..)和reject(...) }); } var p = fn(2);
new Promise(..)模式通常稱為revealing constructor。傳入函數會立即執行(不會像then(..)中的回調一樣異步延遲),它有兩個參數,分別為resolve和reject。這些是promise的決議函數。resolve通常標識完成,reject標識拒絕。
鴨子類型
如何判斷一個對象是不是一個Promise?雖然promise是通過new Promise(..)創建的,但是無法通過instanceof Promise來檢測,最主要的原因是promise可能來自其他的窗口,檢查無法識別這樣的Promise實例。
識別Promise的方法被定義為是否具有then方法。也就是說,任何對象和函數,如果有then(包括原型鏈上)方法,就認為是一個Promise對象。
邏輯大概如下:
if ( p !== null && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function' ) { //這是一個promise對象 }
Promise特點
1、對一個Promise調用then時,提供的回調永遠會被異步調用。
2、只要promise被決議,提供給then的回調會被自動調用,永遠返回一個值。
promise與競態
雖然說一旦promise被決議,后續then方法的回調一定會被調用,但是決議本身未被執行,只能通過別的機制來強制執行:
//必須返回一個promise function delay(time) { return new Promise(function(resolve, reject) { setTimeout(function() { reject('time out'); }, time); }); } //3秒內未決議就會被reject Promise.race(p, delay(3000)).then(function() { //success }, function(err) { //failed })
Promise異常
考慮下面的代碼:
var p = new Promise(function(resolve, reject) { //異常 fo(); }) p.then(function(data) { console.log(data); }, function() { //這個reject接受了異常 並又搞出了一個異常 foo(); }).then(function() { }, function() { //這個reject處理了最后的異常 console.log(1); });
可以看出,每一個then方法會返回一個promise,並且一旦決議就不會改變,異常會轉接到下一個then的reject。
Promise的回調
看起來promise也是利用回調函數完成異步操作,但是有一些不同。
關於Promise.resolve(..)
如果向Promise.resolve(..)傳遞一個非Promise、非thenable(即有then方法的對象或函數)的立即值,就會得到一個用這個值填空的promise,相當於包裝。
下面兩種情況,p1和p2的行為是一樣的:
var p1 = new Promise(function(resolve, reject) { resolve(42); }); var p2 = Promise.resolve(42); console.log(p2); //Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 42}
而如果向Promise.resolve(..)傳遞一個真正的Promise,就只會返回同一個promise。
var p1 = Promise.resolve(42); var p2 = Promise.resolve(p1); console.log(p1 === p2); //true
如果向Promise.resolve(..)傳遞了一個非Promise的thenable值,前者會試圖展開這個值,而且展開過程會持續到提取出一個具體的非類Promise的最終值。
比如說:
var p = { then: function(resolve, reject) { resolve(42); // reject('123'); } }; p.then(function resolve(val) { console.log(val); }, function reject(err) { console.log(err); }); Promise.resolve(p); //打印42 得到一個Promise
Promise.resolve(..)可以接受任何thenable,將其解封為非thenable的值。從Promise.resolve(..)得到的肯定是一個Promise,是一個可信任的值。
將一個未知工具封裝Promise並解析,可以這樣:
Promise.resolve('tool').then(function(val) { console.log(val); });
這樣,可以將所有未知封裝為Promise,保證異步調用。
鏈式調用
可以把多個Promise連接到一起以表示一系列異步步驟:
1、每次調用then(..),它都會創建並返回一個新的Promise,我們可以將其鏈接起來。
2、不管從then(..)調用的完成回調返回的值是什么,都會被設置為被鏈接Promise。
不太懂啊:
var p = Promise.resolve(2); var p2 = p.then(function(v) { console.log(v); //2 return v * 2; }); p2.then(function(v) { console.log(v); //4 });
通過Promise.resolve對2進行封裝,調用第一個then方法得到數據2。接着返回一個包含4的Promise封裝對象,由調用then方法獲取到4。
只要通過return和then方法,就能實現Promise鏈式調用。
Ajax案例
鏈式調用可以與ajax完美結合,如下例:
//假設存在方法ajax(url,callback) function request(url) { return new Promise(function(resolve, reject) { //ajax的回調函數是promise的resolve()函數 ajax(url, resolve); }); } //異步請求返回數據data request('http://...').then(function(data) { //將返回的數據拼接到第二個url作為參數再次請求 return request('http://...?data=' + data); }).then(function(data2) { //得到最終數據 console.log(data2); });
如果then方法參數未提供resolve或者reject,會有一個默認的函數生成,如下例:
Promise.resolve(2).then( //function(v){ // return v; //} null, function() { console.log(1); }).then(function(v) { console.log(v); //2 }, //默認錯誤處理函數 //function(err) { // throw err; //} );
小結:1、調用Promise.resolve(..)與then(..)總會返回一個Promise。
2、決議后返回的值可以通過鏈式調用解決掉。