Promise 對象用於處理異步請求,保存一個異步操做最終完成(或失敗)的結果數組
語法promise
new Promise( /* executor */ function(resolve, reject) {...} ); /* 來自谷歌翻譯 promise:承諾,約定 resolve:解決,決定,下決心 reject:拒絕,駁回,抵制 */
參數:app
promise 構造函數接受一個 executor 函數做爲參數,該函數的兩個參數分別是 resolve 和 reject,它們是兩個函數(executor 函數 在 Promise 構造函數返回新對象以前被調用)。異步
resolve 函數被調用時,將 promise 對象從 「未完成」 變爲 「成功」 (即 pending --> fulfilled)函數
reject 函數被調用時,將 promise 對象從 「未完成」 變爲 「失敗」 (即 pending --> rejected)fetch
描述編碼
promise 對象是一個代理對象(代理一個值),被代理的值在 promise 對象建立時多是未知的。它容許你爲異步操做的成功和失敗分別綁定相應的處理方法,使得異步方法能夠像同步方法那樣返回值,但不是當即返回最終執行結果,而是一個能表明將來出現的結果的 promise 對象。url
上面提到過,一個 promise 對象有三個狀態:spa
• pending:初始狀態,不是成功或失敗狀態prototype
• fulfilled:成功完成
• rejected:失敗
pending 狀態可能觸發 fulfilled 狀態並傳遞一個值給相應的狀態處理方法,也可能觸發 rejected 狀態並傳遞失敗信息。當其中任一種狀況發生時,promise 對象的 then 方法綁定的處理方法就會被調用。
(then 方法包含兩個參數:onfulfilled 和 onrejected,也都是函數。當 promise 對象的狀態爲 fulfilled 時調用,調用 then 的 onfulfilled 方法;反之,調用 onrejected 方法。因此異步操做的完成和綁定處理方法之間不存在競爭)
then 方法的使用語法:
promise.then(function(value) { // onfulfilled }, function(error) { // 第二個參數是可選的,不必定要提供 // onrejected });
示例:
let myFirstPromise = new Promise(function(resolve, reject){ // 當異步代碼執行成功時,調用resolve()方法;失敗時,調用reject()方法 // 此處,使用定時器來模擬異步代碼,實際編碼多是XHR請求或HTML5的一些API方法 setTimeout(function(){ //resolve('成功!') //代碼執行成功,調用resolve()方法 resolve('成功!') }, 2000) }) myFirstPromise.then(function(successMessage){ // successMessage 是上面調用resolve()方法傳入的值 // successMessage 參數不必定非要是字符串類型,這裏只是舉個例子 console.log('Yay!'+successMessage) }) console.log('看看個人位置在哪裏?')
運行結果:
瞭解Promise的基本用法之後,經過一個小栗子來了解Promise的應用場景
// 平常開發中,可能會遇到一個函數的參數依賴於其它函數的返回值,若是其它函數的返回值不能先於執行該函數前返回,就不會獲得咱們預期的結果 // 此例中,執行add函數時,傳入的getValueX方法直接返回2,而getValueY方法存在1秒鐘的延時,當前拿不到返回值,最終致使求和失敗 function add(valueX, valueY) { console.log(valueX + valueY) } add(getValueX(), getValueY()) // NaN (valueX -> 2, valueY -> undefined) function getValueX() { return 2 } // 模仿一個異步請求,1秒後返回數字3 function getValueY() { setTimeout(() => { return 3 }, 1000) }
function add(promiseX, promiseY) { return Promise.all([promiseX, promiseY]).then(values => { return values[0] + values[1] }) } console.log('2秒之後查看x+y求和的結果') add(fetchX(), fetchY()).then(sum => { console.log('x + y 的和爲:' + sum) // x + y 的和爲:3 }) function fetchX() { return new Promise(resolve => { resolve(1) }).then(res => { return res }) } function fetchY() { return new Promise(resolve => { setTimeout(resolve, 2000, 2) }).then(res => { return res }) }
對比之下,有了Promise對象,就能夠清晰地將異步操做以同步操做的流程表達出來
注意:
let promise = new Promise((resolve, reject) => { console.log('做出承諾:"未來"必定會執行!') resolve('resolved') }) promise.then(res => { console.log(res) }) console.log('test') // 做出承諾:"未來"必定會執行! // test // resolved
let promise = new Promise((resolve, reject) => { resolve('ok') throw new Error('error') }) promise.then(res => console.log(res)) .catch(err => console.log(err)) // ok
此例中,Promise 在 resolve語句後面再拋出錯誤,因爲此時Promise狀態已經變成 resolved,再拋出錯誤就無效了
let p1 = new Promise(reject => { reject(new Error('P1 fail... ...')) }) let p2 = new Promise(resolve => { resolve(p1) }) p2.then(res => console.log(res)) .catch(err => console.log(err)) // Error: P1 fail... ...
四、調用 resolve 或 reject 並不會終結 Promise 的參數函數的執行
new Promise((resolve, reject) => { resolve(1) console.log(2) }).then(res => { console.log(res) }) // 2 // 1
// 在resolve或reject前面加上return new Promise((resolve, reject) => { return resolve(1) console.log(2) }).then(res => { console.log(res) }) // 1 // 後續操做放在then方法裏 new Promise((resolve, reject) => { resolve(2) }).then(res => { console.log(res) console.log(1) }) // 2 // 1
經過Promise異步加載圖片的示例:
function loadImageAsync(url) { return new Promise((resolve, reject) => { let image = new Image() image.onload = function() { resolve(url) } image.onerror = function() { reject(new Error(`can't find the image: ${url}`)) } image.src = url document.body.appendChild(image) }) } loadImageAsync('https://www.baidu.com/img/xinshouye_c9d9de2ff40fa160f807f75f34db4ad0.gif')
Promise.prototype.then()
它的做用是爲 Promise 實例添加狀態改變時的回調函數,then 方法的第一個參數是 resolved 狀態的回調函數,第二個參數可選,是 rejected 狀態的回調函數
promise.then(function(res) { // success }, function(err) { // error })
儘管then 方法能夠接受第二個參數,但通常不建議在 then 方法裏定義第二個參數(rejected 狀態的回調函數),建議使用 catch 方法
Promise.prototype.catch()
它的做用是指定發生錯誤時的回調函數,至關於 then 方法的第二個參數 promise.then(null, rejection)
catch 方法不只能夠處理異步操做拋出的錯誤,還能夠捕獲then 方法在運行中拋出的錯誤。並且在語義結構上更加接近同步的寫法(try /catch)
{ // 捕獲 promise 實例狀態從 pending 變爲 rejected 的錯誤 let promise = new Promise((resolve, reject) => { reject(new Error('test error')) }) promise.then(res => console.log(res)) .catch(err => console.log(err)) // Error: test error } { // 捕獲 then 方法中運行時拋出的錯誤 let promise = new Promise(resolve => { resolve(x / 0) }) promise.then(res => console.log(res)) .catch(err => console.log(err)) // ReferenceError: x is not defined }
Promise.prototype.finally()
finally 方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。
finally 方法的回調函數不接受任何參數,所以,也就不知道 Promise 的狀態是 fulfield 仍是 rejected,代表 finally 方法不依賴於 Promise 的執行結果,與狀態無關
let promise = new Promise((resolve, reject) => { if(5 > 3) { resolve('計算正確') } else{ reject(new Error('計算出錯了')) } }) promise.then(res => console.log(res)) .catch(err => console.log(err)) .finally(() => console.log('無論計算對錯,都要執行fianlly')) // 計算正確 // 無論計算對錯,都要執行fianlly
Promise.all()
Promise.all 方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例
const p = Promise.all([p1, p2, p3]);
all 方法接受一個數組做爲參數,參數必須是 Promise 實例
p 的狀態由 p一、p二、p3 決定,分紅兩種狀況
(1)、p一、p二、p3 的狀態都變成 fulfilled,p 的狀態纔會變成 fulfilled,此時 p一、p二、p3 的返回值組成一個數組,傳遞給 p 的回調函數
(2)、p一、p二、p3 之中有一個被 rejected,p 的狀態就會變成 rejected,此時第一個被 rejected 的實例的返回值,會傳遞給 p 的回調函數
應用場景:要求頁面同時加載3張圖片,只要有一個加載失敗就會報錯
function loadImgAsync(src) { return new Promise((resolve, reject) => { let img=document.createElement('img'); img.src=src; img.onload=function(){ resolve(img); } img.onerror=function(err){ reject(err); } }) } function showImgs(imgs) { imgs.forEach(function(img){ document.body.appendChild(img); }) } Promise.all([ loadImgAsync('http://www.tietuku.com/static/image/icon3.png'), loadImgAsync('http://www.tietuku.com/static/image/icon2.png'), loadImgAsync('http://www.tietuku.com/static/image/icon1.png') ]).then(showImgs)
Promise.race()
Promise.race 方法一樣用於將多個 Promise 實例,包裝成一個新的 Promise 實例
const p = Promise.race([p1, p2, p3]);
Promise.all 方法看起來像是 邏輯與 的運算,只有數組中的 Promise 實例對象所有執行成功,新的 Promise 實例對象纔會執行 resolved 回調函數
Promise.race 方法則像是 邏輯或 的運算,就像 race 的中文意思:競賽。只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數
將上例需求稍做更改,只要有一個圖片加載成功,便可加載到頁面中,這個時候就要把all 改爲 race 了
function loadImgAsync(src) { return new Promise((resolve, reject) => { let img=document.createElement('img'); img.src=src; img.onload=function(){ resolve(img); } img.onerror=function(err){ reject(err); } }) } function showImgs(img){ let p=document.createElement('p'); p.appendChild(img); document.body.appendChild(p) } Promise.race([ loadImgAsync('http://www.tietuku.com/static/image/icon3.png'), loadImgAsync('http://www.tietuku.com/static/image/icon2.png'), loadImgAsync('http://www.tietuku.com/static/image/icon1.png') ]).then(showImgs)