Promise 讓人又恨又愛的存在,恨是由於面試的時候會圍繞它出不少題,又繞又頭疼,愛是真香,誰都逃不過真香定律。前端
Promise 是異步編程的一個新的解決方案,阮一峯:ECMAScript 6 入門中給出對 promise 的含義是:所謂 Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。 Promise 是一個構造函數,它有兩個特色:git
一般咱們來講,Promise 主要是解決異步回調地獄。什麼是回調地獄?
es6
從網上找了幾張圖,你們能夠感覺一下被回調地獄所支配的恐懼: 回調地獄最大的缺點就是代碼可讀性差,編寫費勁。 接下來咱們來看一下 Promise 的基本用法:github
new Promise((resolve, reject)=>{
... if(success) { resolve(value); }else { reject(error); } }) 複製代碼
以前說過,Promise 是一個構造函數,它接收一個函數參數,這個函數中接收兩個參數,一個是 resolve 還有一個是 rejected,這兩個參數均爲函數,而且這兩個參數不用本身部署,JS 引擎會自動部署。 resolve 函數的做用是當異步函數成功時,將成功後的值傳遞出去,同時也是將狀態從 pending 變爲 resolved,reject 函數的做用是當異步函數失敗後,將失敗的錯誤信息傳遞出去,同時也是將狀態從 pending 變爲 rejected。面試
當 Promise 實例建立成功後,能夠執行其原型上的 then 方法,then 方法一樣接收兩個函數參數,第一個是接收的 resolve 回調的結果,第二個是 reject 回調的結果,第二個參數是非必填的。編程
new Promise((resolve, reject) => {
resolve(1); }).then( (value) => console.log(value) // 1 ); new Promise((resolve, reject) => { reject("出現錯誤"); }).then( (error) => console.log(error) // 出現錯誤 ); 複製代碼
由於 Promise 的對象時當即建立的,因此在 resolve 和 reject 函數以前的操做都會當即執行:數組
new Promise((resolve, reject) => {
console.log(2); resolve(1); }).then( (value) => console.log(value) // 2 1 ); 複製代碼
Promise 執行 then 方法後會返回回來一個新的 Promise 對象,因此能夠採用鏈式調用。promise
new Promise((resolve) => {
resolve(1); }) .then((value) => value + 1) .then((value) => console.log(value)); // 2 複製代碼
第一個 then 函數的返回值,能夠做爲參數傳給第二個 then 函數。若是第一個 then 函數返回的依舊是一個 Promise 對象呢?便是一個 Promise 封裝的異步操做:markdown
new Promise((resolve) => {
resolve(1); }) .then( (value) => new Promise((resolve) => { resolve(3); }) ) .then((value) => console.log(value)); // 3 複製代碼
此時第二個 then 函數傳入的參數,即爲第一個 then 函數返回的 Promise 對象的 resolved 狀態下傳遞的值。也能夠說只有第一個 then 返回的 Promise 執行狀態成功時,第二個 then 函數纔會執行。異步
除了 then 函數外,在 Promise 原型上還有一個 catch 函數,此函數時當 Promise 內部異步出現錯誤的時候即爲 rejected 狀態時,才執行。
new Promise((resolve, reject)=>{
throw new Error('test') }).catch(err=>console.log(err)) // Error: test 等同於: new Promise((resolve, reject)=>{ throw new Error('test') }).then(null, err=>console.log(err)) // Error: test 複製代碼
當 then 第二個參數和 catch 函數同時存在時,將不會執行 catch 函數:
new Promise((resolve, reject) => {
throw new Error("test"); }) .then(null, (err) => console.log(err)) // Error: test .catch((err) => console.log(err)); 複製代碼
那此時的 catch 捕獲的是哪一個 Promise 的錯誤呢?捕獲的是前一個 Promise 的錯誤,即 then 函數返回回來的 Promise 錯誤:
new Promise((resolve, reject) => {
throw new Error("test"); }) .then(null, (err) => { throw new Error("test1"); }) .catch((err) => console.log(err)); // Error: test1 複製代碼
Promise 的錯誤有一種相似冒泡機制,當 catch 以前沒有沒有任何函數截獲錯誤,那終究會被 catch 截獲。
new Promise((resolve, reject) => {
throw new Error("test"); }) .then() .catch((err) => console.log(err)); // Error: test 複製代碼
只要 catch 前任何一個 Promise 報錯,那終究會被 catch 截獲。 一般狀況下,不建議使用 then 函數的第二個參數來進行錯誤的捕獲,如上所說的 catch 寫法能夠捕獲前面 then 方法執行中的錯誤,也更接近同步的寫法(try/catch)。所以,建議老是使用 catch()方法,而不使用 then()方法的第二個參數。 then()返回一個新的 Promise 對象,catch()一樣返回一個 Promise 對象,一樣可使用鏈式調用:
new Promise((resolve, reject) => {
throw new Error("test"); }) .then() .catch((err) => console.log(err)) // Error: test .then(() => console.log(2)); // 2 複製代碼
當 catch 捕獲完錯誤後,會接着執行下面的 then 方法,當沒有錯誤拋出時,則會跳過 catch,直接執行後面的 then 方法。可是以後的 Promise 出現錯誤時,以前的 catch 就捕獲不到了。
new Promise((resolve, reject) => {
throw new Error("test"); }) .then() .catch((err) => { throw new Error("test1"); }) // Error: test .then(() => console.log(2)); 複製代碼
catch 後面能夠鏈式調用 then 方法,一樣也能夠調用 catch 方法,後面的 catch 方法是接收前一個 catch 方法所拋出的錯誤。
在 ES8 中新加入了一個方法,即 finally,此方法不一樣於 then 和 catch,它不跟蹤與 Promise 對象狀態的改變,即無論 Promise 的最終狀態如何,都會執行這個方法,同時 finally 不一樣於 then 和 catch 地方就是,它不接受任何參數。
new Promise((resolve) => {
resolve(1); }) .then((value) => console.log(value)) // 1 .finally(() => console.log(2)); // 2 new Promise(() => { throw new Error("test"); }) .catch((err) => console.log(err)) // Error test .finally(() => console.log(2)); // 2 複製代碼
finally 一樣返回一個新的 Promise 對象,用法和以前的 then 和 catch 同樣,這塊就不作過多的講解了。
除了上述 Promise 原型上的方法外,Promise 還有不少其餘的 API。
經過字面意思就能看出來,這個方法是‘所有’意思,因而可知能夠接受多個 Promise 對象。 該方法接受具備 Iterator 接口而且每一個成員都是 Promise 實例的參數,並返回一個新的 Promise 對象。
let p = Promise.all([
new Promise((resolve) => { resolve(1); }), new Promise((resolve) => { resolve(2); }), new Promise((resolve) => { resolve(3); }), ]); 複製代碼
而且,只有當接受的參數中全部成員的狀態都爲 resolved 的時候,p 的狀態纔會爲 resolved,若是有一個成員的狀態爲 rejected,那 p 的狀態就爲 rejected。 當全部成員的狀態均爲 resolved 的時候,會將每一個成員 resolved 狀態下的值拼成數組傳遞給 p 的回調函數。
let p = Promise.all([
new Promise((resolve) => { resolve(1); }), new Promise((resolve) => { resolve(2); }), new Promise((resolve) => { resolve(3); }), ]); p.then((result) => console.log(result)); // [1, 2, 3] 複製代碼
當有一個成員的狀態爲 rejected 的時候,則會將第一個 rejected 狀態的值返給 p 的 catch 方法。
let p = Promise.all([
new Promise((resolve) => { resolve(1); }), new Promise((resolve, rejecct) => { rejecct(2); }), new Promise((resolve) => { resolve(3); }), ]); p.catch((err) => console.log(err)); 複製代碼
若是有一個成員爲 rejected 狀態,而且自身調用了 catch 方法,那將不會走 p 對象的 catch 方法,這一點要注意。
race 翻譯成中文是競賽的意思,他表示多個 Promise 對象,哪一個成員率先改變狀態,那 Promise.race 返回的 Promise 對象的狀態變爲何,並將值轉遞給 p 的回調函數,它和 Promise.all 接收的參數同樣。
let p = Promise.race([
new Promise((resolve) => { setTimeout(() => resolve(1), 100); }), new Promise((resolve) => { setTimeout(() => resolve(2), 50); }), new Promise((resolve) => { setTimeout(() => resolve(3), 200); }), ]); p.then((result) => console.log(result)); // 2 let p = Promise.race([ new Promise((resolve) => { setTimeout(() => resolve(1), 100); }), new Promise((resolve, reject) => { setTimeout(() => reject(2), 50); }), new Promise((resolve) => { setTimeout(() => resolve(3), 200); }), ]); p.catch((err) => console.log(err)); // 2 複製代碼
該方法是 ES2020 新加入的,和 all 同樣,返回一個新的 Promise 對象,接收一組 Promise 對象,可是和 all 區別的是,當無論每一個成員的 Promise 是什麼狀態,只要執行結束,則返回的 Promise 對象就會執行結束。
let p = Promise.allSettled([
new Promise((resolve) => { resolve(1); }), new Promise((resolve, rejecct) => { rejecct(2); }), new Promise((resolve) => { resolve(3); }), ]); p.then((value) => console.log(JSON.stringify(value))); // [{status:"fulfilled",value:1},{status:"rejected",reason:2},{status:"fulfilled",value:3}] 複製代碼
有時候異步請求並不在乎是否可以成功,這個時候這個方法就很符合場景了,而且返回一個數組,數組中每一個對象有兩個狀態,一個是 fulfilled,另外一個是 rejected,返回以後能夠進行篩選,查看錯誤信息。
將一個對象轉化爲一個 Promise 對象。
Promise.resolve("foo");
等價於; new Promise((resolve) => resolve("foo")); 複製代碼
當 Promise.resolve 的參數是一個 Promise 實例時,原封不動的返回這個實例:
let p = new Promsie((resolve) => resolve(1));
let p1 = Promise.resolve(p); p === p1; // true 複製代碼
當參數是一個 thenable 對象時,即含有 then 方法的對象時,會返回一個 Promise 對象,並當即執行 then 方法。
let thenable = {
then: function (resolve, reject) { resolve(1); }, }; let p = Promise.resolve(thenable); p.then((value) => console.log(value)); // 1 複製代碼
當參數是否是一個 thenable 對象時,因爲參數不是一個異步的,因此當 Promise.resolve 後,直接的狀態就是 resolved 的狀態,因此 then 後就會輸出原值。
1. 參數是個普通對象
let obj = { name: '1' } let p = Promise.resolve(obj); p.then(value=>console.log(value)) // {name: '1'} 2. 參數是個基本數據類型 let num = '1'; let p = Promise.resolve(num); p.then(value=>console.log(value)) // 1 複製代碼
當不傳參數的時候,返回的就是一個帶有 resolved 狀態的 Promise 對象。
返回一個狀態爲 rejected 的 Promise 對象,傳入的參數做爲錯誤信息做爲後續方法傳遞的參數。
let num = "1";
let p = Promise.reject(num); p.then(null, (err) => console.log(err)); // 1 複製代碼
當參數是 thenable 對象時,返回的不是 error 信息而是 thenable 對象。
let thenable = {
then: function (resolve, reject) { reject(1); }, }; let p = Promise.reject(thenable); p.then(null, (err) => console.log(err === thenable)); // true 複製代碼
相關文章:
以爲還能夠的,麻煩走的時候能給點個贊,你們一塊兒學習和探討!
還能夠關注個人博客但願能給個人github上點個Start,小夥伴們必定會發現一個問題,個人全部用戶名幾乎都與番茄有關,由於我真的很喜歡吃番茄❤️!!!
想跟車不迷路的小夥還但願能夠關注公衆號 前端老番茄 或者掃一掃下面的二維碼👇👇👇。
我是一個編程界的小學生,您的鼓勵是我不斷前進的動力,😄但願能一塊兒加油前進。
本文使用 mdnice 排版