es6 Promise的學(xué)習(xí)筆記

promise是什么

promise對(duì)象可以理解為一次執(zhí)行的一步操作,使用promise對(duì)象之后可以用一種鏈?zhǔn)秸{(diào)用的方式來(lái)組織代碼,使代碼更加的直觀!而且由于有promise.all 這樣的方法存在,可以讓同事執(zhí)行多個(gè)操作變得簡(jiǎn)單。

為什么要使用promise?舉個(gè)栗子。有一個(gè)需求,多個(gè)接口異步請(qǐng)求數(shù)據(jù),然后利用這些數(shù)據(jù)來(lái)進(jìn)行一系列的操作。代碼如下:

$.ajax({
    url:'a.php',
    datatype:"json",
    success:function(data){
         // 獲取data數(shù)據(jù) 傳給下一個(gè)請(qǐng)求
         var id=data.id;
         $.ajax({
             url:"b.php",
             datatype:'json',
              data:{"id":id},
             success:function(){
                 
             }
         })
    }
    
})

這樣的寫(xiě)法的原理是,當(dāng)執(zhí)行一些一步操作的時(shí)候,我們需要知道操作是不是完成了,所以當(dāng)執(zhí)行完成的時(shí)候會(huì)返回一個(gè)回調(diào)函數(shù),然我們知道操作已經(jīng)完成了,但是,這樣做有幾個(gè)缺點(diǎn):

  1. 在需要多個(gè)操作的時(shí)候,會(huì)導(dǎo)致回調(diào)函數(shù)的嵌套,代碼不夠直觀,這就是地獄回調(diào)(Callback hell)
  2. 如果前后兩個(gè)參數(shù)在不需要傳參的情況下,同樣需要等待上一個(gè)操作完成再實(shí)行下一個(gè)操作

因此針對(duì)這種情況下,我們可以使用promise對(duì)象來(lái)解決上面的問(wèn)題了~

如何創(chuàng)建promise

可以用new 來(lái)調(diào)用promise的構(gòu)造器來(lái)進(jìn)行實(shí)例化,如代碼:

new promise(function(resolve,reject){
    //異步請(qǐng)求,
    //成功調(diào)用resolve  往下傳遞參數(shù) 且只接受一個(gè)參數(shù)
    //失敗調(diào)用reject  往下傳遞參數(shù) 且只接受一個(gè)參數(shù)
    
})

對(duì)通過(guò)new 生成的promise對(duì)象為了設(shè)置其值在resolve(成功) / reject(失敗) 時(shí)調(diào)用的回調(diào)函數(shù),可以使用promise.then()實(shí)例方法。

代碼如下:

promise.then(onFulfilled, onRejected)

resolve(成功)時(shí)調(diào)用onFulfilled方法,reject(失敗) 時(shí) 調(diào)用onRejected方法;

Promise.then, 成功和失敗時(shí)都可以使用,如果出現(xiàn)異常的情況下可以采用

promise.then(undefined,onRejected) 這種方式,只指定onRejected回調(diào)函數(shù)即可,不過(guò)針對(duì)這種情況下我們有更好的選擇是使用catch這個(gè)方法;代碼如下:

promise.catch(onRejected);

resolve 和 reject

首先看下面promise 的代碼:

function helloWorld (ready) {
    return new Promise(function (resolve, reject) {
        if (ready) {
            resolve("Hello World!");
        } else {
            reject("Good bye!");
        }
    });
}

helloWorld(true).then(function (message) {
    alert(message);
}, function (error) {
    alert(error);
});

helloWorld方法接受一個(gè)參數(shù),如果為true 打印 Hello World!如果為false打印Good bye!;helloWorld()返回的是一個(gè)promise對(duì)象。

在promise中有兩個(gè)很重的方法:resolve 和 reject;

==resolve== :可以使 Promise 對(duì)象的狀態(tài)改變成成功,同時(shí)傳遞一個(gè)參數(shù)用于后續(xù)成功后的操作,在這個(gè)例子當(dāng)中就是 Hello World! 字符串。

==reject== :可以使 Promise 對(duì)象的狀態(tài)改變成失敗,同時(shí)將錯(cuò)誤的信息傳遞到后續(xù)錯(cuò)誤處理的操作。

理解Promise.resolve和理解Promise.reject

一般情況下我們都會(huì)使用new Promise()來(lái)創(chuàng)建promise對(duì)象,但是我們也可以使用promise.resolve 和 promise.reject這兩個(gè)方法;

Promise.resolve(value)的返回值也是一個(gè)promise對(duì)象,我們可以對(duì)返回值進(jìn)行.then調(diào)用;如下代碼:

promise.resolve(10).then(function(value){
    console.log(value)//10
})

resolve(10)代碼中,會(huì)讓promise對(duì)象進(jìn)入確定(resolve狀態(tài)),并將參數(shù)11傳遞給后面的then所指定的onFulfilled 函數(shù);

Promise.reject 也是new Promise的快捷形式,也創(chuàng)建一個(gè)promise對(duì)象,比如如下代碼:

new Promise(function(resolve,reject){
     reject(new Error("錯(cuò)誤!"));
});

可以變成:

Promise.reject(new Error("錯(cuò)誤!"));

綜合看看使用resolve方法和reject方法的demo如下:

function getready(ready){
  return  new Promise(function(resolve,reject){
        if(ready){
            resolve("ok")
        }else{
            reject("No");
        }
    })
    
};

getready(true).then(function(msg){
    console.log(msg)
},function(error){
     console.log(error)
})

上面的代碼的含義是給getready方法傳遞一個(gè)參數(shù),返回的是一個(gè)promise對(duì)象,如果為true的話,那么調(diào)用promise對(duì)象中的resolve()方法,并且把其中的參數(shù)傳遞給后面的then第一個(gè)函數(shù)內(nèi),因此打印出 ok, 如果為false的話,會(huì)調(diào)用promise對(duì)象中的reject()方法,則會(huì)進(jìn)入then的第二個(gè)函數(shù)內(nèi),會(huì)打印No;

Promise對(duì)象的三種狀態(tài)

  1. Fulfilled 可以理解為成功的狀態(tài)
  2. Rejected 可以理解為失敗的狀態(tài)
  3. Pending 既不是 Fulfilld 也不是 Rejected 的狀態(tài),可以理解為 Promise 對(duì)象實(shí)例創(chuàng)建時(shí)候的初始狀態(tài)

比如Promise對(duì)象中的resolve方法就是調(diào)用then對(duì)象的第一個(gè)函數(shù),也就是成功的狀態(tài);而reject方法就是調(diào)用then對(duì)象的第二個(gè)函數(shù),也就是失敗的狀態(tài);

理解Promise異步調(diào)用的操作

如以下代碼:

var promise = new Promise(function(resolve){
    console.log(1);
    resolve(3);
});
promise.then(function(value){
    console.log(value);
});
console.log(2);


//輸出順序:1 2 3

上面的代碼輸出結(jié)果為: 1,2,3;首先代碼從上往下執(zhí)行,首先輸出的1,然后調(diào)用resolve(3)這個(gè)方法,這個(gè)時(shí)候,Promise變?yōu)榱舜_定狀態(tài),即調(diào)用onFullfilled的方法,Promise.then() 成功與否都可以使用的,因此第一個(gè)函數(shù)是成功調(diào)用,但是promise 是以異步方式調(diào)用的,所以先執(zhí)行,console.log(2),最后輸出3

理解then()

在上面 getready()的例子當(dāng)中利用了 then(onFulfilld, onRejected) 方法來(lái)執(zhí)行一個(gè)任務(wù)打印 "ok",在多個(gè)任務(wù)的情況下 then 方法同樣可以用一個(gè)清晰的方式完成。

function printHello (ready) {
    return new Promise(function (resolve, reject) {
        if (ready) {
            resolve("Hello");
        } else {
            reject("Good bye!");
        }
    });
}
function printWorld () {
    alert("World");
}

function printExclamation () {
    alert("!");
}

printHello(true)
    .then(function(message){
        alert(message);
    })
    .then(printWorld)
    .then(printExclamation);

上述例子通過(guò)鏈?zhǔn)秸{(diào)用的方式,按順序打印出了相應(yīng)的內(nèi)容。then 可以使用鏈?zhǔn)秸{(diào)用的寫(xiě)法原因在于,每一次執(zhí)行該方法時(shí)總是會(huì)返回一個(gè) Promise 對(duì)象。另外,在 then onFulfilled 的函數(shù)當(dāng)中的返回值,可以作為后續(xù)操作的參數(shù),因此上面的例子也可以寫(xiě)成:

function printHello (ready) {
    return new Promise(function (resolve, reject) {
        if (ready) {
            resolve("Hello");
        } else {
            reject("Good bye!");
        }
    });
}
function printWorld () {
    alert("World");
}

function printExclamation () {
    alert("!");
}

printHello(true).then(function (message) {
    return message;
}).then(function (message) {
    return message  + ' World';
}).then(function (message) {
    return message + '!';
}).then(function (message) {
    alert(message);
});

//輸出Hellow world!
var promise1 = new Promise(function(resolve){
    resolve(2);
});
promise1.then(function(value){
    return value * 2;
}).then(function(value){
    return value * 2;
}).then(function(value){
    console.log("1"+value);
});

打印出18,即 "1" + 222 = 18;

使用方法鏈的then,使多個(gè)then方法連接在一起了,因此函數(shù)會(huì)嚴(yán)格執(zhí)行 resolve -- then --- then -- then的順序執(zhí)行,并且傳遞每個(gè)then方法的value的值都是前一個(gè)promise對(duì)象中return的值;因此最后的結(jié)果就是18了;

理解catch()

Promise.catch()方法是promise.then(undefined,onRejected)方法的一個(gè)別名,該方法用來(lái)注冊(cè)當(dāng)promise對(duì)象狀態(tài)變?yōu)镽ejected的回調(diào)函數(shù)。

如下代碼:

var promise = Promise.reject(new Error("message"));
promise.catch(function(error){
    console.log(error.message);//message
});

現(xiàn)在我們?cè)倩剡^(guò)頭一剛開(kāi)始我們討論的為什么要使用promise的原因的問(wèn)題了,比如2個(gè)ajax請(qǐng)求,后一個(gè)ajax請(qǐng)求需要獲取到前一個(gè)ajax請(qǐng)求的數(shù)據(jù),我們之前在使用jquery寫(xiě)代碼是如下的:

$.ajax({
    url:'a.php',
    datatype:"json",
    success:function(data){
         // 獲取data數(shù)據(jù) 傳給下一個(gè)請(qǐng)求
         var id=data.id;
         $.ajax({
             url:"b.php",
             datatype:'json',
             success:function(){
                 
             }
         })
    }
    
})

現(xiàn)在我們學(xué)習(xí)了then方法后,我們可以重新編寫(xiě)上面的代碼變成如下:

var ajaxPromise = new Promise(function(resolve){
    resolve();
});
ajaxPromise.then(fucntion(){
    $.ajax({
    url:'a.php',
    datatype:"json",
    success:function(data){
         // 獲取data數(shù)據(jù) 傳給下一個(gè)請(qǐng)求
         var id=data.id;
         return id;
    }
    
})
}).then(function(id){
        $.ajax({
        url:'',
        dataType:'json',
        data:{"id":id},
        success: function(data){
            console.log(data);
        }
    });
})

理解Promise.all

Promise.all可以接受一個(gè)元素為Promise對(duì)象的數(shù)組作為參數(shù),當(dāng)這個(gè)數(shù)組里面所有的promise對(duì)象都變?yōu)閞esolve時(shí),該方法才會(huì)返回。

如下代碼:

var promise1 = new Promise(function(resolve){
    setTimeout(function(){
        resolve(1);
    },3000);
});
var promise2 = new Promise(function(resolve){
    setTimeout(function(){
        resolve(2);
    },1000);
});
Promise.all([promise1,promise2]).then(function(value){
    console.log(value); // 打印[1,2]
});

輸出的是1,2。如上我們看到promise1對(duì)象中的setTimeout是3秒的時(shí)間,而promise2對(duì)象中的setTimeout是1秒的時(shí)間,但是在Promise.all方法中會(huì)按照數(shù)組的原先順序?qū)⒔Y(jié)果返回;在我們平時(shí)的需求中,或許有這種情況的需求,比如我們需要發(fā)2個(gè)ajax請(qǐng)求時(shí),不管他們的先后順序,當(dāng)這2個(gè)ajax請(qǐng)求都同時(shí)成功后,我們需要執(zhí)行某些操作的情況下,這種情況非常適合;

理解Promise.all

如上可知:Promise.all 在接收到的所有對(duì)象promise都變?yōu)镕ulFilled或者 Rejected狀態(tài)之后才會(huì)繼續(xù)后面的處理,但是Promise.race的含義是只要有一個(gè)promise對(duì)象進(jìn)入FulFilled或者Rejected狀態(tài)的話,程序就會(huì)停止,且會(huì)繼續(xù)后面的處理邏輯;

如下代碼:

// `delay`毫秒后執(zhí)行resolve
function timerPromise(delay){
    return new Promise(function(resolve){
        setTimeout(function(){
            resolve(delay);
        },delay);
    });
}
// 任何一個(gè)promise變?yōu)閞esolve或reject 的話程序就停止運(yùn)行
Promise.race([
    timerPromise(1),
    timerPromise(32),
    timerPromise(64),
    timerPromise(128)
]).then(function (value) {
    console.log(value);    // => 1
});

輸出的是1,如上代碼創(chuàng)建了4個(gè)promise對(duì)象,這些promise對(duì)象分別在1ms,32ms,64ms,128ms后變?yōu)榇_定狀態(tài),并且在第一個(gè)變?yōu)榇_定狀態(tài)后1ms后,then函數(shù)就會(huì)被調(diào)用,這時(shí)候resolve()方法給傳遞的值為1,因此執(zhí)行then的回調(diào)函數(shù)后,值變?yōu)?;

我們?cè)賮?lái)看看當(dāng)一個(gè)promise對(duì)象變?yōu)榇_定狀態(tài)(FulFiled)的時(shí)候,他們后面的promise對(duì)象是否還在運(yùn)行呢?我們繼續(xù)看如下代碼運(yùn)行:

var runPromise = new Promise(function(resolve){
    setTimeout(function(){
        console.log(1);
        resolve(2);
    },500);
});
var runPromise2 = new Promise(function(resolve){
    setTimeout(function(){
        console.log(3);
        resolve(4);
    },1000);
});
// 第一個(gè)promise變?yōu)閞esolve后程序停止
Promise.race([runPromise,runPromise2]).then(function(value){
    console.log(value);
});

輸出的是: 1 2 3,。

如上代碼是使用定時(shí)器調(diào)用的,上面是2個(gè)promise對(duì)象,我們看到第一個(gè)promise對(duì)象過(guò)500毫秒后加入到執(zhí)行隊(duì)列里面去,如果執(zhí)行隊(duì)列沒(méi)有其他線程在運(yùn)行的時(shí)候,就執(zhí)行該定時(shí)器,所以第一次打印1,然后調(diào)用resolve(2); 接著調(diào)用promise.race方法,該方法只要有一個(gè)變?yōu)槌晒顟B(tài)(FulFiled)的時(shí)候,程序就會(huì)停止,因此打印出2,同時(shí)后面的promise對(duì)象接著執(zhí)行,因此打印出3,但是由于promise.race()該方法已經(jīng)停止調(diào)用了,所以resolve(4)不會(huì)有任何輸出;因此最后輸出的是1,2,3;

由此我們得出結(jié)論,當(dāng)一個(gè)promise對(duì)象變?yōu)?FulFilled)成功狀態(tài)的時(shí)候,后面的promise對(duì)象并沒(méi)有停止運(yùn)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Promiese 簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,語(yǔ)法上說(shuō),Pr...
    雨飛飛雨閱讀 3,380評(píng)論 0 19
  • 本文適用的讀者 本文寫(xiě)給有一定Promise使用經(jīng)驗(yàn)的人,如果你還沒(méi)有使用過(guò)Promise,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,333評(píng)論 6 19
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,189評(píng)論 0 16
  • 00、前言Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)...
    夜幕小草閱讀 2,139評(píng)論 0 12
  • //本文內(nèi)容起初摘抄于 阮一峰 作者的譯文,用于記錄和學(xué)習(xí),建議觀者移步于原文 概念: 所謂的Promise,...
    曾經(jīng)過(guò)往閱讀 1,252評(píng)論 0 7