目錄
?
1promise的定義
2基本用法
3. Promise.prototype.then()
4. Promise.prototype.catch()
5. Promise.all()
6. Promise.race()
?7. Promise.resolve()
8. Promise.reject()
9. 兩個有用的附加方法
10總結
1promise的定義
Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6 將其寫進了語言標 準,統一了用法,原生提供了 Promise 對象。
所謂 Promise ,簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可 以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。 Promise 對象有以下兩個特點。 (1)對象的狀態不受外界影響。 Promise 對象代表一個異步操作,有三種狀態: pending (進行中)、 fulfilled (已成功)和 rejected (已失敗)。 只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是“承諾”,表 示其他手段無法改變。 (2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。 Promise 對象的狀態改變,只有兩種可能:從 pending 變為 fulfilled 和從 pending 變為 rejected 。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你 再對 Promise 對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。 有了 Promise 對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外, Promise 對象提供統一的接口,使得控制異步操 作更加容易。 Promise 也有一些缺點。首先,無法取消 Promise ,一旦新建它就會立即執行,無法中途取消。其次,如果不設置回調函數, Promise 內部拋出的錯誤, 不會反應到外部。第三,當處于 pending 狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完
2基本用法
ES6 規定, Promise 對象是一個構造函數,用來生成 Promise 實例。 下面代碼創造了一個 Promise 實例。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>test</title>
</head><body><script>const promise = new Promise(function(resolve, reject) {// ... some codeif ( /* 異步操作成功 */ ) {resolve(value);} else {reject(error);}});</script>
</body></html>
Promise 構造函數接受一個函數作為參數,該函數的兩個參數分別是 resolve 和 reject 。它們是兩個函數,由 JavaScript 引擎提供,不用自己部署。 resolve 函數的作用是,將 Promise 對象的狀態從“未完成”變為“成功”(即從 pending 變為 resolved),在異步操作成功時調用,并將異步操作的結 果,作為參數傳遞出去; reject 函數的作用是,將 Promise 對象的狀態從“未完成”變為“失敗”(即從 pending 變為 rejected),在異步操作失敗時調 用,并將異步操作報出的錯誤,作為參數傳遞出去。 Promise 實例生成以后,可以用 then 方法分別指定 resolved 狀態和 rejected 狀態的回調函數
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const promise = new Promise(function(resolve, reject) {// ... some codepromise.then(function(value) {// success}, function(error) {// failure});});</script>
</body></html>
then 方法可以接受兩個回調函數作為參數。第一個回調函數是 Promise 對象的狀態變為 resolved 時調用,第二個回調函數是 Promise 對象的狀態變為 rejected 時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受 Promise 對象傳出的值作為參數。 下面是一個 Promise 對象的簡單例子。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done');});}timeout(100).then((value) => {console.log(value); //done});</script>
</body></html>
上面代碼中, timeout 方法返回一個 Promise 實例,表示一段時間以后才會發生的結果。過了指定的時間( ms 參數)以后, Promise 實例的狀態變為 resolved ,就會觸發 then 方法綁定的回調函數。 Promise 新建后就會立即執行。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>let promise = new Promise(function(resolve, reject) {console.log('Promise');resolve();});promise.then(function() {console.log('resolved.');});console.log('Hi!');// Promise// Hi!// resolved</script>
</body></html>
上面代碼中,Promise 新建后立即執行,所以首先輸出的是 Promise 。然后, then 方法指定的回調函數,將在當前腳本所有同步任務執行完才會執行,所 以 resolved 最后輸出
下面是異步加載圖片的例子。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>function loadImageAsync(url) {return new Promise(function(resolve, reject) {const image = new Image();image.onload = function() {resolve(image);};image.onerror = function() {reject(new Error('Could not load image at ' + url));};image.src = url;});}</script>
</body></html>
上面代碼中,使用 Promise 包裝了一個圖片加載的異步操作。如果加載成功,就調用 resolve 方法,否則就調用 reject 方法。 下面是一個用 Promise 對象實現的 Ajax 操作的例子。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const getJSON = function(url) {const promise = new Promise(function(resolve, reject) {const handler = function() {if (this.readyState !== 4) {return;}if (this.status === 200) {resolve(this.response);} else {reject(new Error(this.statusText));}};const client = new XMLHttpRequest();client.open("GET", url);client.onreadystatechange = handler;client.responseType = "json";client.setRequestHeader("Accept", "application/json");client.send();});return promise;};getJSON("/posts.json").then(function(json) {console.log('Contents: ' + json);}, function(error) {console.error('出錯了', error);});</script>
</body></html>
上面代碼中, getJSON 是對 XMLHttpRequest 對象的封裝,用于發出一個針對 JSON 數據的 HTTP 請求,并且返回一個 Promise 對象。需要注意的是, 在 getJSON 內部, resolve 函數和 reject 函數調用時,都帶有參數。 如果調用 resolve 函數和 reject 函數時帶有參數,那么它們的參數會被傳遞給回調函數。 reject 函數的參數通常是 Error 對象的實例,表示拋出的錯 誤; resolve 函數的參數除了正常的值以外,還可能是另一個 Promise 實例,比如像下面這樣
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const p1 = new Promise(function(resolve, reject) {// ...});const p2 = new Promise(function(resolve, reject) {// ...resolve(p1);})</script>
</body></html>
上面代碼中, p1 和 p2 都是 Promise 的實例,但是 p2 的 resolve 方法將 p1 作為參數,即一個異步操作的結果是返回另一個異步操作。 注意,這時 p1 的狀態就會傳遞給 p2 ,也就是說, p1 的狀態決定了 p2 的狀態。如果 p1 的狀態是 pending ,那么 p2 的回調函數就會等待 p1 的狀態改變; 如果 p1 的狀態已經是 resolved 或者 rejected ,那么 p2 的回調函數將會立刻執行。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const p1 = new Promise(function(resolve, reject) {setTimeout(() => reject(new Error('fail')), 3000)})const p2 = new Promise(function(resolve, reject) {setTimeout(() => resolve(p1), 1000)})p2.then(result => console.log(result)).catch(error => console.log(error))// Error: fail</script>
</body></html>
上面代碼中, p1 是一個 Promise,3 秒之后變為 rejected 。 p2 的狀態在 1 秒之后改變, resolve 方法返回的是 p1 。由于 p2 返回的是另一個 Promise,導致 p2 自己的狀態無效了,由 p1 的狀態決定 p2 的狀態。所以,后面的 then 語句都變成針對后者( p1 )。又過了 2 秒, p1 變為 rejected , 導致觸發 catch 方法指定的回調函數。 注意,調用 resolve 或 reject 并不會終結 Promise 的參數函數的執行。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>new Promise((resolve, reject) => {resolve(1);console.log(2);}).then(r => {console.log(r);});// 2// 1</script>
</body></html>
上面代碼中,調用 resolve(1) 以后,后面的 console.log(2) 還是會執行,并且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件循環 的末尾執行,總是晚于本輪循環的同步任務。 一般來說,調用 resolve 或 reject 以后,Promise 的使命就完成了,后繼操作應該放到 then 方法里面,而不應該直接寫在 resolve 或 reject 的后面。 所以,最好在它們前面加上 return 語句,這樣就不會有意外。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>new Promise((resolve, reject) => {return resolve(1);// 后面的語句不會執行console.log(2);})</script>
</body></html>
3. Promise.prototype.then()
Promise 實例具有 then 方法,也就是說, then 方法是定義在原型對象 Promise.prototype 上的。它的作用是為 Promise 實例添加狀態改變時的回調函 數。前面說過, then 方法的第一個參數是 resolved 狀態的回調函數,第二個參數(可選)是 rejected 狀態的回調函數。 then 方法返回的是一個新的 Promise 實例(注意,不是原來那個 Promise 實例)。因此可以采用鏈式寫法,即 then 方法后面再調用另一個 then 方法
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>getJSON("/posts.json").then(function(json) {return json.post;}).then(function(post) {// ...});</script>
</body></html>
上面的代碼使用 then 方法,依次指定了兩個回調函數。第一個回調函數完成以后,會將返回結果作為參數,傳入第二個回調函數。 采用鏈式的 then ,可以指定一組按照次序調用的回調函數。這時,前一個回調函數,有可能返回的還是一個 Promise 對象(即有異步操作),這時后一個 回調函數,就會等待該 Promise 對象的狀態發生變化,才會被調用。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>getJSON("/post/1.json").then(function(post) {return getJSON(post.commentURL);}).then(function funcA(comments) {console.log("resolved: ", comments);}, function funcB(err) {console.log("rejected: ", err);});</script>
</body></html>
上面代碼中,第一個 then 方法指定的回調函數,返回的是另一個 Promise 對象。這時,第二個 then 方法指定的回調函數,就會等待這個新的 Promise 對 象狀態發生變化。如果變為 resolved ,就調用 funcA ,如果狀態變為 rejected ,就調用 funcB 。 如果采用箭頭函數,上面的代碼可以寫得更簡潔。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>getJSON("/post/1.json").then(post => getJSON(post.commentURL)).then(comments => console.log("resolved: ", comments),err => console.log("rejected: ", err));</script>
</body></html>
4. Promise.prototype.catch()
Promise.prototype.catch 方法是 .then(null, rejection) 的別名,用于指定發生錯誤時的回調函數。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>getJSON('/posts.json').then(function(posts) {// ...}).catch(function(error) {// 處理 getJSON 和 前一個回調函數運行時發生的錯誤console.log('發生錯誤!', error);});</script>
</body></html>
上面代碼中, getJSON 方法返回一個 Promise 對象,如果該對象狀態變為 resolved ,則會調用 then 方法指定的回調函數;如果異步操作拋出錯誤,狀態 就會變為 rejected ,就會調用 catch 方法指定的回調函數,處理這個錯誤。另外, then 方法指定的回調函數,如果運行中拋出錯誤,也會被 catch 方法 捕獲。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>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)) s</script>
</body></html>
下面是一個例子。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const promise = new Promise(function(resolve, reject) {throw new Error('test');});promise.catch(function(error) {console.log(error);});// Error: test</script>
</body></html>
上面代碼中, promise 拋出一個錯誤,就被 catch 方法指定的回調函數捕獲。注意,上面的寫法與下面兩種寫法是等價的
?
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>// 寫法一const promise = new Promise(function(resolve, reject) {try {throw new Error('test');} catch (e) {reject(e);}});promise.catch(function(error) {console.log(error);});// 寫法二const promise = new Promise(function(resolve, reject) {reject(new Error('test'));});promise.catch(function(error) {console.log(error);});</script>
</body></html>
比較上面兩種寫法,可以發現 reject 方法的作用,等同于拋出錯誤。 如果 Promise 狀態已經變成 resolved ,再拋出錯誤是無效的
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const 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</script>
</body></html>
上面代碼中,Promise 在 resolve 語句后面,再拋出錯誤,不會被捕獲,等于沒有拋出。因為 Promise 的狀態一旦改變,就永久保持該狀態,不會再變 了。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>getJSON('/post/1.json').then(function(post) {return getJSON(post.commentURL);}).then(function(comments) {// some code}).catch(function(error) {// 處理前面三個Promise產生的錯誤})</script>
</body></html>
上面代碼中,一共有三個 Promise 對象:一個由 getJSON 產生,兩個由 then 產生。它們之中任何一個拋出的錯誤,都會被最后一個 catch 捕獲。 一般來說,不要在 then 方法里面定義 Reject 狀態的回調函數(即 then 的第二個參數),總是使用 catch 方法 Promise 對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個 catch 語句捕獲
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>// badpromise.then(function(data) {// success}, function(err) {// error});// goodpromise.then(function(data) { //cb// success}).catch(function(err) {// error});</script>
</body></html>
上面代碼中,第二種寫法要好于第一種寫法,理由是第二種寫法可以捕獲前面 then 方法執行中的錯誤,也更接近同步的寫法( try/catch )。因此,建議 總是使用 catch 方法,而不使用 then 方法的第二個參數。 跟傳統的 try/catch 代碼塊不同的是,如果沒有使用 catch 方法指定錯誤處理的回調函數,Promise 對象拋出的錯誤不會傳遞到外層代碼,即不會有任何 反應。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const someAsyncThing = function() {return new Promise(function(resolve, reject) {// 下面一行會報錯,因為x沒有聲明resolve(x + 2);});};someAsyncThing().then(function() {console.log('everything is great');});setTimeout(() => {console.log(123)}, 2000);// Uncaught (in promise) ReferenceError: x is not defined// 123</script>
</body></html>
上面代碼中, someAsyncThing 函數產生的 Promise 對象,內部有語法錯誤。瀏覽器運行到這一行,會打印出錯誤提示 ReferenceError: x is not defined ,但是不會退出進程、終止腳本執行,2 秒之后還是會輸出 123 。這就是說,Promise 內部的錯誤不會影響到 Promise 外部的代碼,通俗的說法 就是“Promise 會吃掉錯誤”。 這個腳本放在服務器執行,退出碼就是 0 (即表示執行成功)。不過,Node 有一個 unhandledRejection 事件,專門監聽未捕獲的 reject 錯誤,上面的 腳本會觸發這個事件的監聽函數,可以在監聽函數里面拋出錯誤。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>process.on('unhandledRejection', function(err, p) {throw err;});</script>
</body></html>
上面代碼中, unhandledRejection 事件的監聽函數有兩個參數,第一個是錯誤對象,第二個是報錯的 Promise 實例,它可以用來了解發生錯誤的環境信 息注意,Node 有計劃在未來廢除 unhandledRejection 事件。如果 Promise 內部有未捕獲的錯誤,會直接終止進程,并且進程的退出碼不為 0。 再看下面的例子
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const 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</script>
</body></html>
上面代碼中,Promise 指定在下一輪“事件循環”再拋出錯誤。到了那個時候,Promise 的運行已經結束了,所以這個錯誤是在 Promise 函數體外拋出 的,會冒泡到最外層,成了未捕獲的錯誤。 一般總是建議,Promise 對象后面要跟 catch 方法,這樣可以處理 Promise 內部發生的錯誤。 catch 方法返回的還是一個 Promise 對象,因此后面還可 以接著調用 then 方法
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const 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</script>
</body></html>
上面代碼運行完 catch 方法指定的回調函數,會接著運行后面那個 then 方法指定的回調函數。如果沒有報錯,則會跳過 catch 方法。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>Promise.resolve().catch(function(error) {console.log('oh no', error);}).then(function() {console.log('carry on');});// carry on</script>
</body></html>
上面的代碼因為沒有報錯,跳過了 catch 方法,直接執行后面的 then 方法。此時,要是 then 方法里面報錯,就與前面的 catch 無關了。 catch 方法之中,還能再拋出錯誤。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const 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</script>
</body></html>
上面代碼中, catch 方法拋出一個錯誤,因為后面沒有別的 catch 方法了,導致這個錯誤不會被捕獲,也不會傳遞到外層。如果改寫一下,結果就不一樣 了。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>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]</script>
</body></html>
上面代碼中,第二個 catch 方法用來捕獲,前一個 catch 方法拋出的錯誤。
5. Promise.all()
Promise.all 方法用于將多個 Promise 實例,包裝成一個新的 Promise 實例。 const p = Promise.all([p1, p2, p3]); 上面代碼中, Promise.all 方法接受一個數組作為參數, p1 、 p2 、 p3 都是 Promise 實例,如果不是,就會先調用下面講到的 Promise.resolve 方法, 將參數轉為 Promise 實例,再進一步處理。( Promise.all 方法的參數可以不是數組,但必須具有 Iterator 接口,且返回的每個成員都是 Promise 實 例。) p 的狀態由 p1 、 p2 、 p3 決定,分成兩種情況。 (1)只有 p1 、 p2 、 p3 的狀態都變成 fulfilled , p 的狀態才會變成 fulfilled ,此時 p1 、 p2 、 p3 的返回值組成一個數組,傳遞給 p 的回調函數。 (2)只要 p1 、 p2 、 p3 之中有一個被 rejected , p 的狀態就變成 rejected ,此時第一個被 reject 的實例的返回值,會傳遞給 p 的回調函數。 下面是一個具體的例子
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>// 生成一個Promise對象的數組const promises = [2, 3, 5, 7, 11, 13].map(function(id) {return getJSON('/post/' + id + ".json");});Promise.all(promises).then(function(posts) {// ...}).catch(function(reason) {// ...});</script>
</body></html>
上面代碼中, promises 是包含 6 個 Promise 實例的數組,只有這 6 個實例的狀態都變成 fulfilled ,或者其中有一個變為 rejected ,才會調用 Promise.all 方法后面的回調函數。 下面是另一個例子
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const databasePromise = connectDatabase();const booksPromise = databasePromise.then(findAllBooks);const userPromise = databasePromise.then(getCurrentUser);Promise.all([booksPromise,userPromise]).then(([books, user]) => pickTopRecommentations(books, user));</script>
</body></html>
上面代碼中, booksPromise 和 userPromise 是兩個異步操作,只有等到它們的結果都返回了,才會觸發 pickTopRecommentations 這個回調函數。 注意,如果作為參數的 Promise 實例,自己定義了 catch 方法,那么它一旦被 rejected ,并不會觸發 Promise.all() 的 catch 方法。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const p1 = new Promise((resolve, reject) => {resolve('hello');}).then(result => result).catch(e => e);const p2 = new Promise((resolve, reject) => {throw new Error('報錯了');}).then(result => result).catch(e => e);Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));// ["hello", Error: 報錯了]</script>
</body></html>
上面代碼中, p1 會 resolved , p2 首先會 rejected ,但是 p2 有自己的 catch 方法,該方法返回的是一個新的 Promise 實例, p2 指向的實際上是這個實 例。該實例執行完 catch 方法后,也會變成 resolved ,導致 Promise.all() 方法參數里面的兩個實例都會 resolved ,因此會調用 then 方法指定的回調函 數,而不會調用 catch 方法指定的回調函數。 如果 p2 沒有自己的 catch 方法,就會調用 Promise.all() 的 catch 方法。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const p1 = new Promise((resolve, reject) => {resolve('hello');}).then(result => result);const p2 = new Promise((resolve, reject) => {throw new Error('報錯了');}).then(result => result);Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));// Error: 報錯了</script>
</body></html>
6. Promise.race()
Promise.race 方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。 const p = Promise.race([p1, p2, p3]); 上面代碼中,只要 p1 、 p2 、 p3 之中有一個實例率先改變狀態, p 的狀態就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給 p 的回調函 數。 Promise.race 方法的參數與 Promise.all 方法一樣,如果不是 Promise 實例,就會先調用下面講到的 Promise.resolve 方法,將參數轉為 Promise 實 例,再進一步處理。 下面是一個例子,如果指定時間內沒有獲得結果,就將 Promise 的狀態變為 reject ,否則變為 resolve 。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const 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));</script>
</body></html>
上面代碼中,如果 5 秒之內 fetch 方法無法返回結果,變量 p 的狀態就會變為 rejected ,從而觸發 catch 方法指定的回調函數
?7. Promise.resolve()
有時需要將現有對象轉為 Promise 對象, Promise.resolve 方法就起到這個作用。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const jsPromise = Promise.resolve($.ajax('/whatever.json'));</script>
</body></html>
上面代碼將 jQuery 生成的 deferred 對象,轉為一個新的 Promise 對象
(1)參數是一個 Promise 實例 如果參數是 Promise 實例,那么 Promise.resolve 將不做任何修改、原封不動地返回這個實例。 (2)參數是一個 thenable 對象
(3)參數不是具有 then 方法的對象,或根本就不是對象
(4)不帶有任何參數
8. Promise.reject()
Promise.reject(reason) 方法也會返回一個新的 Promise 實例,該實例的狀態為 rejected
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const p = Promise.reject('出錯了');// 等同于const p = new Promise((resolve, reject) => reject('出錯了'))p.then(null, function(s) {console.log(s)});// 出錯了</script>
</body></html>
上面代碼生成一個 Promise 對象的實例 p ,狀態為 rejected ,回調函數會立即執行。 注意, Promise.reject() 方法的參數,會原封不動地作為 reject 的理由,變成后續方法的參數。這一點與 Promise.resolve 方法不一致
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>const thenable = {then(resolve, reject) {reject('出錯了');}};Promise.reject(thenable).catch(e => {console.log(e === thenable)})// true</script>
</body></html>
上面代碼中, Promise.reject 方法的參數是一個 thenable 對象,執行以后,后面 catch 方法的參數不是 reject 拋出的“出錯了”這個字符串,而是 thenable 對象
9. 兩個有用的附加方法
done() Promise 對象的回調鏈,不管以 then 方法或 catch 方法結尾,要是最后一個方法拋出錯誤,都有可能無法捕捉到(因為 Promise 內部的錯誤不會冒泡到 全局)。因此,我們可以提供一個 done 方法,總是處于回調鏈的尾端,保證拋出任何可能出現的錯誤
finally() finally 方法用于指定不管 Promise 對象最后狀態如何,都會執行的操作。它與 done 方法的最大區別,它接受一個普通的回調函數作為參數,該函數不 管怎樣都必須執行
10總結
本博客源于本人閱讀相關書籍和視頻總結,創作不易,謝謝點贊支持。學到就是賺到。我是歌謠,勵志成為一名優秀的技術革新人員。
歡迎私信交流,一起學習,一起成長。
推薦鏈接 其他文件目錄參照
“睡服“面試官系列之各系列目錄匯總(建議學習收藏)
創作挑戰賽 新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔 為你收集整理的“睡服”面试官系列第二篇之promise(建议收藏学习) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。