在瞭解Promise之前,要先知道什麼是非同步,什麼是同步。 大概可以這麼理解,所謂的非同步,就是操作跟操作之間是沒關係的,這就同時帶來一個特點,可以同時進行多個操作。 而同步操作之間是相關的,同時只能做一件事,必須等前一件事做完了,後面的才能進行。 非同步有多個優點,但也有缺點,那就是程式碼會更復雜。而同步的優點就是程式碼簡單。
比方說用非同步操作請求一個電商網站的資料,大概是這樣的:
ajax('/banners',function(banner_data){
ajax('/hotsItems',function(hotsItems_data){
ajax('/slides',function(slides_data){
//......
},function(){
alert("error")
})
},function(){
alert("error")
})
},function(){
alert("error")
})
//做多重的非同步操作,會導致經典的回撥地獄
複製程式碼
這個時候用多重巢狀,會顯得特別麻煩,而用同步操作會顯得簡單,它會走完第一個資料之後,再走下一個:
//同步版,假如有個ajax_async:
let banner_data = ajax_async('/banners');
let hotsItems_data = ajax_async('/hotsItems');
let slides_data = ajax_async('/slides');
...
//但使用者體驗差,也沒回卡死
複製程式碼
用非同步操作,效能高,使用者體驗好,但是程式碼複雜;但同步操作,頁面可能會卡死,也用不得。
Promise--消除非同步操作
Promise是非同步程式設計的一種解決方案,比傳統的解決方案--回撥函式和事件,更合理更強大。用同步一樣的方式,來書寫非同步程式碼
。
本質上,一個promise是某個函式返回的物件,你可以把回撥函式繫結在這個物件上,而不是把回撥函式當作引數傳進函式。
//當我們需要一個promise的時候,需要new一個promise物件,裡面接收一個函式作為引數,裡面非同步的程式碼都寫在函式裡面,這個函式有兩個引數:resolve,reject
let p = new Promise(function(resolve,reject){
//非同步程式碼
//resolve--成功
//reject--失敗
});
//-------------
//此處用promise封裝一個ajax操作
//此處已引入一個jquey檔案
let p = new Promise(function(resolve,reject){
$.ajax({
url:'檔案1路徑',
dataType:'json',
success(data){
//成功的時候呼叫resolve
resolve(data)
},
error(err){
//失敗的時候呼叫reject
reject(err)
}
})
});
//當promise呼叫有結果了,會呼叫then,裡面有兩個函式作為引數,
p.then(function(data){
//成功了呼叫這個函式
console.log(data)
alert('成功'+data)
},function(err){
//失敗會呼叫這個函式
console.log(err)
alert('失敗'+err)
})
複製程式碼
假如要封裝兩個promise,要如何操作?promise物件上有一個方法all(),裡面接收一個陣列作為引數,可以把兩個promise扔進去,然後再使用then方法
let p1 = new Promise(function(resolve,reject){
$.ajax({
url:'檔案1路徑',
dataType:'json',
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
});
let p2 = new Promise(function(resolve,reject){
$.ajax({
url:'檔案2路徑',
dataType:'json',
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
});
Promise.all([
p1,p2
]).then(function(arr){
//全都成功
//會有一個陣列作為結果,裡面裝的是檔案1 2 的資料,所以要分解出兩個結果
let [res1,res2] = arr;
console.log(res1);
console.log(res2);
},function(){
//至少有一個失敗
})
複製程式碼
這時候會發現一個問題:這裡要寫兩個Promise物件,但它們只有地址不一樣,其他都一樣,那可以封裝一個方法,把建立好的Promise物件返回出去:
function createPromise(url){
return new Promise(function(resolve,reject){
$.ajax({
url:url,
dataType:'json',
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
});
}
Promise.all([
createPromise('檔案1路徑');
createPromise('檔案2路徑');
]).then(function(arr){
let [res1,res2] = arr;
console.log(res1);
console.log(res2);
},function(){
//至少有一個失敗
})
複製程式碼
當使用高版本jquery的時候,這個jquery會自帶Promise。
Promise.all([
$.ajax({url:'檔案1路徑',dataType:'json'}),
$.ajax({url:'檔案2路徑',dataType:'json'})
]).then(function(){
//成功
},function(){
//失敗
})
複製程式碼
有了Promise之後非同步,大概是這樣的;
Promise.all([$.ajax(),$.ajax()]).then(result=>{
//成功
},err=>{
//失敗
})
//非同步的本質沒有變,有非同步的優勢,不影響使用者體驗,寫法上也沒比同步複雜多少
複製程式碼
Promise的其他用法
Promise.race:可以同時讀多個資源,誰先來先讀誰,用法跟all類似
Promsie.race([
$.ajax({url:'http://a1.baidu.com/data'}),
$.ajax({url:'http://a2.baidu.com/data'}),//如果a2快,先讀a2
$.ajax({url:'http://a3.baidu.com/data'}),
$.ajax({url:'http://a4.baidu.com/data'}),
$.ajax({url:'http://a5.baidu.com/data'})
])
複製程式碼
以上是Promise的簡單入門,接下來還有Promise進階及原理實現,歡迎關注。