Promise入門(上)

MoTong發表於2018-03-17

在瞭解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進階及原理實現,歡迎關注。

相關文章