『ES6知識點總結』Promise

『ES6知識點總結』變量的解構賦值

本文主要內容如下:
1 Promise是什麼?
1.1 特點:
1.2 三種狀態:
1.3 用處:
1.4 Promise缺點:
1.5 歷史過程
2 生成promise實例
3 promise實例的then()方法
4 resolve函數的參數可以是另一個Promise實例
5 Promise的方法(多種)
5.1 Promise.all()
5.2 Promise.race()
5.3 Promise.reject(reason)
5.4 Promise.resolve(obj)
6 Promise.prototype.catch()的概念
6.1 儘量不定義then的reject方法
6.2 catch方法返回的還是一個Promise對象,因此後面還可以接着調用then方法。
7
7.1 如果前面的promise和then()沒有報錯,則會跳過catch方法。
7.2 拋錯的冒泡性質
7.2.1 如果Promise狀態已經變成resolved,再拋出錯誤是無效的。
7.2.2 Promise在下一輪“事件循環”再拋出錯誤
7.2.3 如果沒有處理錯誤的回調函數,就沒有反應
7.2.4 catch方法之中,還能再拋出錯誤。
8 node.js的unhandledRejection
9 done()
10 finally()

Promise是什麼?
01、是一個對象,用來傳遞異步操作的消息。
02、代表了某個未來纔會知道結果的事件(通常是一個異步操作)。
03、提供一個統一的API,可以進一步處理,控制異步操作。

特點:

01、對象的狀態不受外界影響。

02、代表一個異步操作。

三種狀態:

Pending(進行中)
Resolved(已完成,又稱Fulfilled)
Rejected(已失敗)。

(1)默認的Promise對象是等待態(Pending)。
只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
這也是Promise這個名字的由來,它的英語意思就是“許諾”,表示其他手段無法改變。

(2)一旦狀態改變了,就不會再變了,會一直保持這個結果。
任何時候都可以得到這個結果。

Promise對象的狀態改變,只有兩種:

從Pending變爲Resolved。
從Pending變爲Rejected。

這與事件(Event)不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

用處:
用Promise對象可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。
可以判斷多個異步事件的完成,

Promise缺點:
無法取消Promise,一旦新建它就會立即執行,無法中途取消。
如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。
當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

歷史過程

生成promise實例
Promise是一個構造函數,可以生成Promise實例。

接受一個函數A作爲參數,該函數A有2個參數,是resolve和reject(拒絕),它們是函數,由JS引擎提供,不用自己部署。

resolve(value)函數的作用是,將Promise對象的狀態從“未完成”變爲“成功”(即從Pending變爲Resolved),在異步操作成功時被調用,並將異步操作的結果,作爲自己的參數。

reject(error)函數的作用是,將Promise對象的狀態從“未完成”變爲“失敗”(即從Pending變爲Rejected),在異步操作失敗時調用,並將異步操作報出的錯誤,作爲自己的參數。

reject函數的參數通常是Error對象的實例,表示拋出的錯誤。

例子:
reject(new Error(this.statusText));

在promise實例的構造函數中,可以只返回resolve(value)或reject(error);
例子:
let pro = new Promise((resolve,reject)=>{resolve(value)});

寫法:
創造了一個Promise實例。

const promise = new Promise(function(resolve, reject) {
    if ( /* 異步操作成功 */ ) { resolve(value); } 
    else { reject(error); }
});

promise實例的then()方法
這個方法在實例的狀態改變時會觸發它的參數函數。
當promise實例轉爲成功(Resolved)狀態時,觸發then()的第一個函數參數。
當promise實例轉爲失敗(Rejected)狀態時,觸發then()的第二個函數參數。(第二個函數參數可選)

這兩個函數參數都接受promise實例傳出的值作爲參數。也就是resolve(value)和reject(value)的value值。

then()方法是定義在原型對象Promise.prototype上的,被所有promise實例繼承。

promise.then(
    function (value) { // success
    },
    function (value) { // failure
    }
);

箭頭函數方式:
promise.then( (value)=>{}, (error)=>{} )

如果then()中有設置新的promise實例,則then()方法返回這個promise對象。
否則將默認構建一個新的promise對象,並繼承調用then()方法的promise的狀態(成功或失敗)。

then方法return返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。
因此可以採用鏈式寫法,即then方法後面再調用另一個then方法。

這個函數返回undefined,則then()方法構建一個默認的Promise對象,並且這個對象擁有then()方法所屬的Promise對象的狀態。

var p = new Promise(function (resolve) {
        resolve();//直接標誌執行態
    }), temp;
temp = p.then(function () {
        //傳入執行態函數,不返回值
    });
 temp.then(function () {
        console.log('fulfilled');//擁有p的狀態
    });
console.log(temp === p);//默認構建的promise,但已經和p不是同一個對象,輸出false

如果對應狀態所執行的函數返回一個全新的Promise對象,則會覆蓋掉當前Promise,代碼如下:

var p = new Promise(function (resolve) {
    resolve();//直接標誌執行態
}), temp;
temp = p.then(function () {
    //返回新的promise對象,和p的狀態無關
    return new Promise(function (resolve, reject) {
        reject();//標誌拒絕態
    });
});
temp.then(function () {
    console.log('fulfilled');
}, function () {
    console.log('rejected');//輸出rejected
});




resolve函數的參數可以是另一個Promise實例
表示當前異步操作的取決於另一個異步操作的結果。
舉個例子來說,張三到李四家拿鑰匙,取決於李四在家不在家。
如果李四在家,那麼張三可以成功拿到鑰匙。如果李四不在家,張三就拿不到鑰匙。

有2個promise實例p1和p2,
p2的resolve(p1),那麼,p1的狀態決定了p2的狀態。
如果p1的狀態是Pending,那麼p2的回調函數就會等待p1的狀態改變,在此之前不會有結果。
如果p1的狀態已經是Resolved或者Rejected,那麼p2的回調函數將會立刻執行。

比如像下面這樣。

var p1 = new Promise(function(resolve, reject) { // ...});
var p2 = new Promise(function(resolve, reject) { // ...
    resolve(p1);
})

【】例子:
p1是一個Promise,3秒之後變爲rejected。p2的狀態由p1決定,1秒之後,p2調用resolve方法,但是此時p1的狀態還沒有改變,因此p2的狀態也不會變。又過了2秒,p1變爲rejected,p2也跟着變爲rejected。


var p1 = new Promise(function(resolve, reject) {
    setTimeout(() => reject(new Error('fail')), 3000);
});

var p2 = new Promise(function(resolve, reject) {
    setTimeout(() => resolve(p1), 1000)
});
p2.then(result => console.log(result))
p2.catch(error => console.log(error))
    // Error: fail

【】例子:
下面是一個Promise對象的簡單例子。
timeout函數中,會返回新生成的Promise實例,其中,定時器setTimeout會在100ms後調用resolve("done");當調用resolve後,觸發then綁定的回調函數。
zyx456:'done'是resolve的參數。
等效於:

function timeout(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve("done"),ms);
    });
}
timeout(100).then((value) => {
    console.log(value);
});




【】例子:
下面是異步加載圖片的例子。

function loadImageAsync(url) {

return new Promise(function(resolve, reject) {
    var image = new Image();
    image.onload = function() { resolve(image); };
    image.onerror = function() { reject(new Error('Could not load image at ' + url)); };
    image.src = url;
});

}

【】例子:
下面是一個用Promise對象實現的Ajax操作的例子。
getJSON是對XMLHttpRequest對象的封裝,用於發出一個針對JSON數據的HTTP請求,並且返回一個Promise對象。

var getJSON = function (url) {
var promise = new Promise(function (resolve, reject) {
    var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {
        if (this.readyState !== 4) {
            return;
        }
        if (this.status === 200) {
            resolve(this.response);
        } else {
            reject(new Error(this.statusText));
        }
    };
});
return promise;
};
getJSON("/posts.json").then(function (json) {
    console.log('Contents: ' + json);
}, function (error) {
    console.error('出錯了', error);
});

箭頭函數寫法:

getJSON("/post/1.json").then(
post => getJSON(post.commentURL)).then(
comments => console.log("Resolved: ", comments),
err => console.log("Rejected: ", err));

Promise的方法(多種)

Promise.all()

【01】用於將多個Promise實例,包裝成一個新的Promise實例。
接受一個數組作爲參數,數組元素都是Promise實例。
如果參數不是Promise實例,會調用Promise.resolve()轉變爲Promise實例。

Promise.all()方法的參數不一定是數組,但是必須具有iterator接口,且返回的每個成員都是Promise實例。

【】返回的新的Promise實例的狀態:

所有Promise參數的都變成Resolved,p的狀態纔會變成Resolved,參數的返回值組成一個數組,傳遞給p的回調函數。

只要有一個參數被rejected,p的狀態就變成rejected,此時第一個reject的實例的返回值,會傳遞給p的回調函數。

(小z:類似於且操作,且假或真,只有所有的是成功的纔可以。你想想,一系列的操作只有都成功才叫成功。)

var p = Promise.all([p1,p2,p3]);

例子

// 生成一個Promise對象的數組
var promises = [2, 3, 5, 7, 8].map(function(id){return getJSON("/post/" + id + ".json");});

Promise.all(promises).then(function(posts) { // ...
}).catch(function(reason){ // ...
});

Promise.race()
race:賽跑。
【01】將多個Promise實例,包裝成一個新的Promise實例。
返回這個新promise實例,新promise實例的狀態爲第一個改變的參數實例的狀態。它的值也是該參數實例傳遞出來的值。

03,參數如果不是Promise實例,就會調用Promise.resolve方法,將參數轉爲Promise實例,再進一步處理。

var p = Promise.race([p1,p2,p3]);

例子:

var p = Promise.race([
    fetch('/resource-that-may-take-a-while'),
    new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
    })])
p.then(response => console.log(response))
p.catch(error => console.log(error))


Promise.reject(reason)

【】會返回一個新的Promise實例,該實例的狀態爲rejected。
參數reason,會被傳遞給實例的回調函數。


var p = Promise.reject('出錯了');
// 等同於
var p = new Promise((resolve, reject) => reject('foo'))

p.then(null, function (s){  console.log(s)});// 出錯了




Promise.resolve(obj)

【01】將對象轉爲Promise對象。
如果參數不是具有then方法的對象(又稱thenable對象),則返回一個新的Promise對象,且它的狀態爲Resolved。
比如:參數爲字符串時,不屬於異步操作(判斷方法是它不是具有then方法的對象),所以等價於Promise構造函數立即執行resolve("str");

03,如果沒有參數,可以得到一個Promise對象。
04,如果參數是一個Promise實例,則會被原封不動地返回。

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

Promise.resolve等價於下面的寫法。

Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))

例子:

    var p = Promise.resolve('Hello');
p.then(function (s){console.log(s)});// Hello

例子:

var p = Promise.resolve();
p.then(function () { // ...});

Promise.prototype.catch()的概念
【01】是 promise實例.then(null, rejection) 的別名,用於指定發生錯誤時的回調函數。

【】例子:
getJSON方法返回一個Promise對象,如果該對象狀態變爲Resolved,則會調用then方法指定的回調函數;
如果異步操作拋出錯誤,狀態就會變爲Rejected,就會調用catch方法指定的回調函數,處理這個錯誤。

getJSON("/posts.json").then(function(posts) { // ...
}).catch(function(error) { // 處理前一個回調函數運行時發生的錯誤
    console.log('發生錯誤!', error);
});

例子:

p.then((val) => console.log("fulfilled:", val)).catch((err) => console.log("rejected:", err));
// 等同於

p.then((val) => console.log(fulfilled: ", val)).then(null, (err) => console.log("
 rejected: ", err));

例子:
Promise拋出一個錯誤,就被catch方法指定的回調函數捕獲。


var promise = new Promise(function(resolve, reject) {
    throw new Error('test') });
promise.catch(function(error) { console.log(error) });// Error: test

儘量不定義then的reject方法
一般來說,不要在then方法裏面定義Reject狀態的回調函數(即then的第二個參數),總是使用catch方法。
第二種寫法要好於第一種寫法,理由是前者更接近同步的寫法(try/catch)。

// bad
promise
    .then(function(data) { // success
    }, function(err) { // error
    });
// good
promise
    .then(function(data) { //cb
        // success
    }).catch(function(err) { // error
    });


catch方法返回的還是一個Promise對象,因此後面還可以接着調用then方法。

var someAsyncThing = function() {
return new Promise(function(resolve, reject) { // 下面一行會報錯,因爲x沒有聲明
    resolve(x + 2);
});
};
someAsyncThing().catch(function(error) {
    console.log('oh no', error);
}).then(function() {
    console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on

如果前面的promise和then()沒有報錯,則會跳過catch方法。

Promise.resolve().catch(function(error) {
    console.log('oh no', error);
}).then(function() {
    console.log('carry on');
});
// carry on


拋錯的冒泡性質
Promise對象的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲爲止。
也就是說,錯誤總是會被下一個catch語句捕獲。

例子:
一共有三個Promise對象:一個由getJSON產生,兩個由then產生。它們之中任何一個拋出的錯誤,都會被最後一個catch捕獲。

getJSON("/post/1.json").then(function(post) {

return getJSON(post.commentURL); }).then(function(comments) { // some code

}).catch(function(error) { // 處理前面三個Promise產生的錯誤
});

如果Promise狀態已經變成resolved,再拋出錯誤是無效的。
Promise在resolve語句後面,再拋出錯誤,不會被捕獲,等於沒有拋出。

var promise = new Promise(function(resolve, reject) {
resolve("ok");

throw new Error('test'); });

promise.then(function(value) { console.log(value) }).catch(function(error) { console.log(error) });// ok

Promise在下一輪“事件循環”再拋出錯誤
結果由於沒有指定使用try...catch語句,就冒泡到最外層,成了未捕獲的錯誤。
因爲此時,Promise的函數體已經運行結束了,所以這個錯誤是在Promise函數體外拋出的。

var promise = new Promise(function(resolve, reject) { 
     resolve("ok");
    setTimeout(function() {
        throw new Error('test') }, 0) });
promise.then(function(value) { console.log(value) });
// ok
// Uncaught Error: test


如果沒有處理錯誤的回調函數,就沒有反應
跟傳統的try/catch代碼塊不同的是,如果沒有使用catch方法指定錯誤處理的回調函數,Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應。

someAsyncThing函數產生的Promise對象會報錯,但是由於沒有指定catch方法,這個錯誤不會被捕獲,也不會傳遞到外層代碼,導致運行後沒有任何輸出。

var someAsyncThing = function() {
    return new Promise(function(resolve, reject) { // 下面一行會報錯,因爲x沒有聲明
        resolve(x + 2);
    });
};
someAsyncThing().then(function() {
    console.log('everything is great');
});



catch方法之中,還能再拋出錯誤。
catch方法拋出一個錯誤,因爲後面沒有別的catch方法了,導致這個錯誤不會被捕獲,也不會傳遞到外層。

var someAsyncThing = function() {
    return new Promise(function(resolve, reject) { // 下面一行會報錯,因爲x沒有聲明
        resolve(x + 2);
    });
};
someAsyncThing().then(function() {
    return someOtherAsyncThing();
 }).catch(function(error) {
    console.log('oh no', error); // 下面一行會報錯,因爲y沒有聲明
    y + 2;
}).then(function() {
    console.log('carry on');
});
// oh no [ReferenceError: x is not defined]


如果改寫一下,結果就不一樣了。
第二個catch方法用來捕獲,前一個catch方法拋出的錯誤。

someAsyncThing().then(function() {
    return someOtherAsyncThing(); }).catch(function(error) {
    console.log('oh no', error); // 下面一行會報錯,因爲y沒有聲明
    y + 2;
}).catch(function(error) {
    console.log('carry on', error);
});
// oh no [ReferenceError: x is not defined]
// carry on [ReferenceError: y is not defined]



node.js的unhandledRejection
Node.js有一個unhandledRejection事件,專門監聽未捕獲的reject錯誤。
unhandledRejection事件的監聽函數有兩個參數,第一個是錯誤對象,第二個是報錯的Promise實例,它可以用來了解發生錯誤的環境信息。


process.on('unhandledRejection', function (err, p) {
  console.error(err.stack)});




done()

Promise對象的回調鏈,不管以then方法或catch方法結尾,要是最後一個方法拋出錯誤,都有可能會漏掉(Promise內部的錯誤不會冒泡到全局)。
因此,我們可以提供一個done方法,總是處於回調鏈的尾端,保證拋出任何可能出現的錯誤。

asyncFunc().then(f1).catch(r1).then(f2).done();

done方法的使用,可以像then方法那樣用,提供Fulfilled和Rejected狀態的回調函數,也可以不提供任何參數。但不管怎樣,done都會捕捉到任何可能出現的錯誤,並向全局拋出。

實現代碼:

Promise.prototype.done = function (resolve, reject) {
    this.then(resolve, reject).catch(function (reason) { // 拋出一個全局錯誤
        setTimeout(() => {
            throw reason
        }, 0);
    });
};




finally()
finally方法用於指定不管Promise對象最後狀態如何,都會執行的操作。
它與done方法的最大區別,它接受一個普通的回調函數作爲參數,該函數不管怎樣都必須執行。

等效於,運行一個then(),在成功和失敗回調函數中,都要運行callback(),
在Promise.resolve()中運行callback()後,在運行then(),返回結果。

下面是一個例子,服務器使用Promise處理請求,然後使用finally方法關掉服務器。

server.listen(0).then(function () { // run test
  }).finally(server.stop);

它的實現:

Promise.prototype.finally = function(callback) {
    return this.then(
        value => Promise.resolve(  callback()  ).then(() => value),
        reason => Promise.resolve(callback()).then(() => {throw reason }));
};







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章