Promise的作用以及基本使用
關於Promise的概念,在實際使用之前對其的理解一直比較模糊,只是停留在一些文件上的描述。在使用中其實可以根據其特性進行一些更佳的實踐。在這裡簡單介紹一下其作用以及基礎用法。
作用
Promise物件可以理解為一次執行的非同步操作,使用promise物件之後可以使用一種鏈式呼叫的方式來組織程式碼;讓程式碼更加的直觀。也就是說,有了Promise物件,就可以將非同步操作以同步的操作的流程表達出來,避免了層層巢狀的回撥函式。總結一下就是可以將原先不可控的回撥通過promise轉為更加可控更清晰的方式表達,更加高效,更便於維護。
示例:未使用promise,回撥必須層層巢狀
$.ajax(url1, function(data1){
// do something1...
$.ajax(url2, function(data2){
// do something2...
$.ajax(url3, function(data3){
// do something3...
})
});
});
複製程式碼
如果有多個巢狀,導致程式碼不夠直觀,邏輯梳理上會更加的困難。並且這幾個操作都是有執行的一個依賴關係,需要等待上一個請求完成才可以進行下一個請求,而這些本身就是非同步,這樣的等待是沒有必要的。所以promise可以幫助我們解決這一部分痛點。
瞭解一下Promise的特點
1、物件的狀態不受外界影響(Promise物件代表一個非同步操作,有三種狀態)。
- pending(執行中)
- Resolved(成功,又稱Fulfilled)
- rejected(拒絕)
其中pending為初始狀態,fulfilled和rejected為結束狀態(結束狀態表示promise的生命週期已結束)。promise只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態.。
2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。
Promise物件的狀態改變,只有兩種可能:
- 從Pending變為Resolved
- 從Pending變為Rejected
只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果
3、Promise物件的缺點:
- 無法取消Promise,一旦新建它就會立即執行,無法中途取消。
- 如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部。
- 當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成。
4、promise相容性:除了IE這種古老的瀏覽器和一些低版本的安卓外,大部分的瀏覽器對於promise的相容性還是很友好的,所以我們可以在谷歌的控制檯直接測試我們的程式碼。
接下來用一些例子來看看具體的使用方法
基本用法
1、首先我們new一個Promise,將Promise例項化
2、然後在例項化的promise可以傳兩個引數,一個是成功之後的resolve,一個是失敗之後的reject
3、Promise例項生成以後,可以用then方法分別指定Resolved狀態和Reject狀態的回撥函式
var promise = function(isReady){
return new Promise(function(resolve, reject){
// do somthing, maybe async
if (isReady){
return resolve('成功執行');
} else {
return reject('出錯了');
}
});
}
//Promise例項生成以後,可以用then方法分別指定Resolved狀態和Reject狀態的回撥函式。
promise(true).then(function(value){
// success,這裡是resolve的回撥函式
console.log(value); //hello world
}, function(err){
// failure,這裡是reject的回撥函式
console.log(err)
})
複製程式碼
上述程式碼是執行成功,返回成功執行,如果想測試一下失敗後的返回值,可以把promise(true).then...這裡改為 promise(false).then...在控制檯試下
鏈式操作
Promise並不只是簡化層層回撥的寫法,更重要的在於是通過傳遞狀態的方式來使回撥方式能夠及時的呼叫,因此,相比於callback,它更靈活,更簡單。下面我們來看看Promise的鏈式操作:
makePromise1()
.then(function(value){
console.log(value);
return makePromise2();
})
.then(function(value){
console.log(value);
return makePromise3();
})
.then(function(value){
console.log(value);
});
function makePromise1(){
var p = new Promise(function(resolve, reject){
//非同步操作
setTimeout(function(){
console.log('非同步1');
resolve('非同步1引數');
}, 2000);
});
return p;
}
function makePromise2(){
var p = new Promise(function(resolve, reject){
//非同步操作
setTimeout(function(){
console.log('非同步2');
resolve('非同步2引數');
}, 2000);
});
return p;
}
function makePromise3(){
var p = new Promise(function(resolve, reject){
//非同步操作
setTimeout(function(){
console.log('非同步3');
resolve('非同步3引數');
}, 2000);
});
return p;
}
複製程式碼
上面的程式碼中,有三個非同步操作,makePromise1,makePromise2,makePromise3。其中第二個和第三個依次執行,也就是上一個操作完成之後才可以進行。會相繼的列印出非同步1,非同步1引數···
Promise的catch方法
var promise = function(isReady){
return new Promise(function(resolve, reject){
if (isReady){
return resolve('成功執行');
} else {
return reject('失敗');
}
});
}
promise(true)
.then(function(value){
console.log('resolved');
console.log(value);
console.log(wawa); //此處的wawa未定義
})
.catch(function(error){
console.log('rejected');
console.log(error);
});
複製程式碼
catch 方法是 then(onFulfilled, onRejected) 方法當中 onRejected 函式的一個簡單的寫法,也就是說可以寫成 then(fn).catch(fn),相當於 then(fn).then(null, fn)使用 catch 的寫法比一般的寫法更加清晰明確,其實可以類比成try/catch,這樣,其中有報錯的地方不會阻塞執行。比如定義了一個未定義wawa,正常來說它上面的程式碼也不會執行,因為被這個報錯阻塞了,有了catch,它上面的程式碼可以正常執行下去
promise.all方法
Promise.all 可以接收一個元素為 Promise 物件的陣列作為引數,當這個陣列裡面所有的 Promise 物件都變為 resolve 時,該方法才會返回。
var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("第一個promise");
}, 3000);
});
var p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("第二個promise");
}, 1000);
});
Promise.all([p1, p2]).then(function (result) {
console.log(result); // ["第一個promise", "第二個promise"]
});
複製程式碼
上面的程式碼中,all接收一個陣列作為引數,p1,p2是並行執行的,等兩個都執行完了,才會進入到then,all會把所有的結果放到一個陣列中返回,所以我們列印出我們的結果為一個陣列。值得注意的是,雖然p2的執行順序比p1快,但是all會按照引數裡面的陣列順序來返回結果。
promise.race方法
race的意思為賽跑,因此,promise.race也是傳入一個陣列,但是與promise.all不同的是,race只返回跑的快的值,也就是說result返回比較快執行的那個。
var p1 = new Promise(function (resolve) {
setTimeout(function () {
console.log(1);
resolve("第一個promise");
}, 3000);
});
var p2 = new Promise(function (resolve) {
setTimeout(function () {
console.log(2);
resolve("第二個promise");
}, 1000);
});
Promise.race([p1, p2]).then(function (result) {
console.log(result);
});
// 結果:
// 2
// 第二個promise
// 1
複製程式碼
在這邊可以看到,傳的值中,只有p2的返回了,但是p1沒有停止,依然有執行。race的應用場景為,比如我們可以設定為網路請求超時。寫兩個promise,如果在一定的時間內如果成功的那個我們沒有執行到,我們就執行失敗的那個