JS中Promise的使用

在JavaScript中代碼都是單線程執行的,所以JavaScript中全部的網絡操做、瀏覽器事件都必須異步執行。在Promise以前JavaScript處理異步的方式都是回調函數。能夠說callback的方式已經是深刻人心,那Promise又是解決什麼問題的呢?看下面一段代碼:ajax

$.get('/getList',function(){
    $.get('/getCount',function(){
        $.get('/getDetail',function(){
            //....
        })
    })
})

這段代碼就是傳統的callback方式處理異步,能夠看到剛剛3級嵌套代碼層級就已經比較亂了,若是再加上一些邏輯代碼那簡直是沒法直視。這就是咱們常說的==回調地獄==問題。代碼可讀性低,難以維護,沒法複用。數組

Promise解決回調地獄

Promise的基本用法promise

var promise = new Promise((resolve,reject)=>{
    setTimeout(function(){
        //這裏異步操做已經執行完了,就能夠經過resolve告訴外界能夠進行其餘操做了
        resolve('ok');
        //reject('no');
    },2000);
})

promise.then(res=>{
    console.log(res); // ok
},err=>{
    console.log(err); // no
})

經過Promise處理異步,先執行異步操做,不關心如何處理結果,經過Promise對象的返回成功仍是失敗,在未來的某個時刻執行結果處理函數。代碼變得扁平化,且易讀易維護。瀏覽器

  • resolve && reject

上面代碼咱們經過 resolve 方法把 Promise 的狀態置爲完成態(Resolved),這時 then 方法就能捕捉到變化,並執行「成功」狀況的回調。
而 reject 方法就是把 Promise 的狀態置爲已失敗(Rejected),這時 then 方法執行「失敗」狀況的回調(then 方法的第二參數)。網絡

Promise實現多層回調

一樣三級回調的代碼咱們再使用Promise重構一遍異步

new Promise((resolve,reject)=>{
    $.get('/getList',res=>{
        resolve(res);
    });
}).then(res=>{
    return new Promise((resolve,reject)=>{
        $.get('/getCount',res=>{
            resolve(res);
        });
    });
}).then(res=>{
    return new Promise((resolve,reject)=>{
        $.get('/getDetail',res=>{
            resolve(res);
        })
    });
}).then(res=>{
    //...
});

能夠看到不管有多少層回調,都不用互相嵌套,只須要等待Promise對象「通知「執行便可。函數

Promise.all

當須要進行多步沒有關聯邏輯的異步操做時,可使用Promise.all線程

Promise.all([
    $.get('/getList'),
    $.get('/getCount'),
    $.get('/getDetail')
]).then(([data1,data2,data3])=>{
    //then回調的參數是一個數組,順組數序就是異步操做書寫順序
    console.log(data1,data2,data3);
},err=>{
    console.log(err);
});
  • all方法中的參數應該是Promise對象,由於ajax函數返回的對象就是promise對象因此這裏是能夠執行的;
  • 此種方式只適用於異步操做之間無邏輯關聯;
  • 不論異步操做執行順序如何,最後都會按照書寫順序返回(data1,data2,data3是按照異步操做書寫順序返回);
  • 若是如其中一個出錯就會執行錯誤回調;

Promise.race

race的用法與all類似,不一樣點就是all會等全部異步操做所有執行完後再執行then回調,而race中只要有一個異步操做執行完成就馬上執行then回調。code

Promise.race([
    new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('函數一執行!');
            resolve('11');
        }, 1000);
    }),
    new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('函數二執行!');
            resolve('22');
        }, 1200);
    })
]).then(result=>{
    console.log('then執行');
    console.log(result);
},err=>{
    console.log(err);
});

//執行結果爲:
//函數一執行!
//then執行
//11
//函數二執行!

能夠看到函數一執行明顯要比函數二快,因此執行了函數一後當即執行了then回調,==注意這時函數二依然會執行==,可是執行後不會再觸發then回調的執行。對象

Promise錯誤處理

  • 第一種方式,經過then
new Promise((resolve,reject)=>{
    //...
}).then(resSuccess=>{
    //成功
},resError=>{
    //失敗
});
  • 第二種方式,catch捕獲
new Promise((resolve,reject)=>{
    //...
}).then(resSuccess=>{
    //成功
}).catch(resError=>{
    //失敗
});

==注==:catch方式更經常使用,由於不單單能捕獲到reject傳遞的參數,還能夠捕獲到成功的回調中發生的錯誤。


(*Deferred對象及其方法)

jQuery 用 $.Deferred 實現了 Promise 規範。

function fn1() {
   var def = $.Deferred();
   //執行異步操做
   setTimeout(function () {
       console.log('函數1!');
       def.resolve('函數1執行完畢回調');
       //def.reject('函數1執行完畢回調');
   }, 1000);
   return def.promise();
}

//then方法第一個參數接收成功回調,第二個參數是接收失敗回調
fn1().then(res => {
    console.log(res);
},err=>{
    console.log(err);
});

$.Deferred()方法返回一個對象,咱們能夠稱之爲Deferred對象,該對象包含一些方法如:then、done、fail 等。
jQuery就是用這個Deferred對象來實現Promise規範的。

對於多級回調的狀況也可使用then()方法進行鏈式調用:

function fn1() {
    var def = $.Deferred();
    //執行異步操做
    setTimeout(function () {
        console.log('執行函數1!');
        def.resolve('函數1執行完畢回調');
    }, 1000);
    return def.promise();
};

function fn2() {
    var def = $.Deferred();
    //執行異步操做
    setTimeout(function () {
        console.log('執行函數2!');
        def.resolve('函數2執行完畢回調');
    }, 1000);
    return def.promise();
};

fn1().then(res => {
    console.log(res);
    //能夠鏈式調用的核心在於返回一個Deferred對象
    return fn2();
}).then(res => {
    console.log(res);
});

//執行函數1!
//函數1執行完畢回調
//執行函數2!
//函數2執行完畢回調

done() && fail()
done與fail的用法與then類似,實際就是一個語法糖。

function fn1() {
   var def = $.Deferred();
   setTimeout(function () {
       console.log('執行函數1!');
       def.resolve('函數1執行完畢回調');
       // def.reject('函數1執行失敗回調');
   }, 1000);
   return def.promise();
};

fn1().then(function (res) {
    console.log(res);
}, function (err) {
    console.log(err);
});

fn1().done(function (res) {
    console.log(res);
}).fail(function (err) {
    console.log(err);
});

always()
Defferred對象上還有一個always方法,不管異步操做返回什麼結果都會執行always回調。

function fn1() {
    var def = $.Deferred();
    setTimeout(function () {
        console.log('執行函數1!');
        def.resolve('函數1執行完畢回調');
        // def.reject('函數1執行失敗回調');
    }, 1000);
    return def.promise();
};

fn1().then(function (res) {
    console.log(res);
}, function (err) {
    console.log(err);
}).always(() => {
    console.log('不管成功失敗都會進入這裏');
});

因爲Promise的出現,jQuery的Deferred對象已經不多使用了。一些其餘用法在這也不一一舉例了,若是有興趣能夠去官網詳細瞭解。

相關文章
相關標籤/搜索