今天我說下我所理解的prosime,prosime是ES6的構造函數,里面有很多方法,我們之間打印一下promise
它里面有許多方法,等會我們會一一說到。
首先,它有什么用,我們先new 一個看看
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成');
resolve('隨便什么數據');
}, 2000);
});
promise的構造函數接收一個參數,是一個函數,并且有兩個參數:resolve,seject,分別表示異步操作執行成功后的回調函數和異步操作執行失敗后的回調函數。
在上面一串代碼中,我們執行了一個異步操作,也就是seTimeout,2秒后,輸出“執行完成”,并且調用resolve方法。
運行代碼,會在2秒后輸出“執行完成”,注意我在這里只是new了一個對象,并沒有調用它,我們傳進去的函數就已經執行了,這是一個需要注意的細節,所以我們用promise的時候要把它包在一個函數里面如:
function runAsync(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成');
resolve('隨便什么數據');
}, 2000);
});
return p;
}
runAsync()
現在你應該有疑問,這個函數是有什么用,resolve又有什么用。
我們寫好的函數最后會return出一個promise對象,也就是說我們最后會得到這么一個對象,還記得我們剛開始打印的一些方法么,加上這些方法,它就會很強大。
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成');
resolve('隨便一些數據');
}, 1000);
});
return p;
}
runAsync1()
.then(function(results){
console.log(results);
});
這時它會先輸出執行完成,在輸出隨便一些數據,也就是在上面函數執行完了之后,就會執行下面then里面的函數,當然它也是可以回調方法的
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成');
resolve(function(){
alert(1234598)
});
}, 1000);
});
return p;
}
runAsync1()
.then(function(results){
results();
});
你可能會說這東西有什么用,這不就是一個回調函數么,那么問題來了,有多層回調怎么辦?如果回調函數也是一個異步操作,而且執行完,也需要一個相應的回調函數怎么辦?promise的優勢在于,可以在then方法中繼續寫一個promise對象并返回,然后繼續調用then的回調,好比這樣
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成');
resolve('隨便一些數據');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成2');
resolve('隨便一些數據2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('執行完成3');
resolve('隨便一些數據3');
}, 2000);
});
return p;
}
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
它會先后輸出
在then方法中,你也可以直接return數據而不是一個promise對象,在后面then中就可以接受數據啦,比如我們把上面的代碼修改成這樣:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return '直接返回數據'; //這里直接返回數據
})
.then(function(data){
console.log(data);
});
那么輸出結果就變成了
說了這么多,基本對promise對象有了基本的了解,接下來我們看看他還有什么功能,我們光用了resolve,還沒有用reject,它是做什么的呢?事實上,我們前面的例子中只有“執行成功”的回調,還沒有“返回失敗”的回調,reject的作用就是把promise的狀態置為rejected,這樣我們在then中就可以捕捉到了,然后執行“失敗”的回調,看一下代碼
function getNumber(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的隨機數
if(num<=5){
resolve(num);
}
else{
reject('數字太大了');
}
}, 2000);
});
return p;
}
getNumber()
.then(
function(data){
console.log('resolved');
console.log(data);
},
function(reason){
console.log('rejected');
console.log(reason);
}
);
我們在函數中生成一個1--10的隨機數,如果它小于等于5,那么讓它在成功中輸出這個數字,如果他大于5,那么讓它在失敗中輸出“數字太大啦”,在then的方法中有兩個參數,一個是成功的回調方法,一個是失敗的回調方法,多次執行,它就會有時輸出小于5的數字,有時輸出“數字太大了”.
catch的用法
我們知道promise中除了then方法,還有catch方法,它有什么用,其實它和then的第二個參數是一樣的,用來指定reject的回調,用法是這樣
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
效果和寫在then的第二個參數里面一樣,不過它還有另一個作業:在執行resolve的時候(也就是then的第一個參數)時,如果拋出異常啦(代碼出錯啦),那么并不會報錯卡死js,而是到這個catch方法中
all的用法
promise的all方法提供了并行執行異步操作的能力,并且在所有異步操作執行完回調。我們依舊使用上面定義好的runAsync1、runAsync2、runAsync3這3個方法,看以下代碼
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
用promise的all來執行,all接收一個數組參數,里面的值最終都算返回promise對象。這樣,三個異步操作的并行執行的,等到他們都執行完之后才會放到then里面。那么,三個異步操作返回的數據到哪里去啦呢?都在then里面,all會吧所有異步操作的結果放在一個數組中傳給then,就是上面的results。所以上面代碼輸出的結果就是:
有了all,你就可以并行執行多個異步操作,并且在一個回調中處理所有的返回數據。
race的用法
all方法的效果實際上是「誰跑的慢,以誰為準執行回調」,那么相對的就有另一個方法「誰跑的快,以誰為準執行回調」,這就是race方法,這個詞本來就是賽跑的意思。race的用法與all一樣,我們把上面runAsync1的延時改為1秒來看一下:
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
這三個異步操作同樣是并行執行的。結果你應該可以猜到,1秒后runAsync1已經執行完了,此時then里面的就執行了。結果是這樣的:
這個race有什么用呢?使用場景還是很多的,比如我們可以用race給某個異步請求設置超時時間,并且在超時后執行相應的操作,代碼如下:
//請求某個圖片資源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延時函數,用于給請求計時
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('圖片請求超時');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
requestImg函數會異步請求一張圖片,我把地址寫為"xxxxxx",所以肯定是無法成功請求到的。timeout函數是一個延時5秒的異步操作。我們把這兩個返回Promise對象的函數放進race,于是他倆就會賽跑,如果5秒之內圖片請求成功了,那么遍進入then方法,執行正常的流程。如果5秒鐘圖片還未成功返回,那么timeout就跑贏了,則進入catch,報出“圖片請求超時”的信息。運行結果如下: