摘要:總結用方法創建對象用或添加對象的處理函數它的作用是為實例添加狀態改變時的回調函數。方法是的別名,用于指定發生錯誤時的回調函數。
一、為什么需要Promise
Javascript 采用回調函數(callback)來處理異步編程。從同步編程到異步回調編程有一個適應的過程,但是如果出現多層回調嵌套,也就是我們常說的回調金字塔(Pyramid of Doom),絕對是一種糟糕的編程體驗。于是便有了 Promises/A , Promises/A +等規范,用于解決回調金字塔問題。
// 回調金字塔 request("test1.html", "", function(data1) { console.log("第一次請求成功, 這是返回的數據:", data1); request("test2.html", data1, function (data2) { console.log("第二次請求成功, 這是返回的數據:", data2); request("test3.html", data2, function (data3) { console.log("第三次請求成功, 這是返回的數據:", data3); //request... 繼續請求 }, function(error3) { console.log("第三次請求失敗, 這是失敗信息:", error3); }); }, function(error2) { console.log("第二次請求失敗, 這是失敗信息:", error2); }); }, function(error1) { console.log("第一次請求失敗, 這是失敗信息:", error1); }); // 引入 Promise 之后 sendRequest("test1.html", "").then(function(data1) { console.log("第一次請求成功, 這是返回的數據:", data1); }).then(function(data2) { console.log("第二次請求成功, 這是返回的數據:", data2); }).then(function(data3) { console.log("第三次請求成功, 這是返回的數據:", data3); }).catch(function(error) { //用catch捕捉前面的錯誤 console.log("sorry, 請求失敗了, 這是失敗信息:", error); });
什么是Promise?
一個 Promise 對象代表一個目前還不可用,但是在未來的某個時間點可以被解析的值。Promise表示一個異步操作的最終結果。
一個Promise可能有三種狀態:初始狀態(pending)、已完成(fulfilled)、已拒絕(rejected)。pending 狀態的 Promise 對象可能觸發fulfilled 狀態并傳遞一個值給相應的狀態處理方法,也可能觸發失敗狀態(rejected)并傳遞失敗信息。當其中任一種情況出現時,Promise 對象的 then 方法綁定的處理方法(handlers )就會被調用(then方法包含兩個參數:onfulfilled 和 onrejected,它們都是 Function 類型。當Promise狀態為fulfilled時,調用 then 的 onfulfilled 方法,當Promise狀態為rejected時,調用 then 的 onrejected 方法, 所以在異步操作的完成和綁定處理方法之間不存在競爭)。
一個Promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換。
Promise必須實現then方法(可以說,then就是promise的核心),而且then必須返回一個Promise,同一個Promise的then可以調用多次,并且回調的執行順序跟它們被定義時的順序一致。
then方法接受兩個參數,第一個參數是成功時的回調,在Promise由“等待”態轉換到“完成”態時調用,另一個是失敗時的回調,在Promise由“等待”態轉換到“拒絕”態時調用。同時,then可以接受另一個Promise傳入,也接受一個“類then”的對象或方法,即thenable對象。ajax就是一個thenable對象。
Promise狀態變化
優點:
有了 Promise 對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise 對象提供統一的接口,使得控制異步操作更加容易。
//不友好的層層嵌套 loadImg("a.jpg", function() { loadImg("b.jpg", function() { loadImg("c.jpg", function() { console.log("all done!"); }); }); });
缺點:
Promise 也有一些缺點。首先,無法取消 Promise,一旦新建它就會立即執行,無法中途取消。其次,如果不設置回調函數,Promise 內部拋出的錯誤,不會反應到外部。第三,當處于 Pending 狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
返回一個狀態由給定value決定的Promise對象。如果該值是一個Promise對象,則直接返回該對象;如果該值是thenable(即,帶有then方法的對象),返回的Promise對象的最終狀態由then方法執行決定;否則的話(該value為空,基本類型或者不帶then方法的對象),返回的Promise對象狀態為fulfilled,并且將該value傳遞給對應的then方法。通常而言,如果你不知道一個值是否是Promise對象,使用Promise.resolve(value) 來返回一個Promise對象,這樣就能將該value以Promise對象形式使用。
2. Promise.reject(reason) // 生成錯誤的一個promise對象返回一個狀態為失敗的Promise對象,并將給定的失敗信息傳遞給對應的處理方法
3. Promise.prototype.then() // 核心部分返回一個新的Promise。
4. Promise.prototype.catch(onRejected) // 異常捕獲添加一個拒絕(rejection) 回調到當前 promise, 返回一個新的promise。當這個回調函數被調用,新 promise 將以它的返回值來resolve,否則如果當前promise 進入fulfilled狀態,則以當前promise的完成結果作為新promise的完成結果.
5. Promise.all(iterable)這個方法返回一個新的promise對象,該promise對象在iterable參數對象里所有的promise對象都成功的時候才會觸發成功,一旦有任何一個iterable里面的promise對象失敗則立即觸發該promise對象的失敗。這個新的promise對象在觸發成功狀態以后,會把一個包含iterable里所有promise返回值的數組作為成功回調的返回值,順序跟iterable的順序保持一致;如果這個新的promise對象觸發了失敗狀態,它會把iterable里第一個觸發失敗的promise對象的錯誤信息作為它的失敗錯誤信息。Promise.all方法常被用于處理多個promise對象的狀態集合。(可以參考jQuery.when方法)
6. Promise.race(iterable) // 最先執行的promise結果當iterable參數里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數調用父promise綁定的相應句柄,并返回該promise對象。
如果有一個Promise對象執行完成了,后面的還會不會再繼續執行了呢?在ES6 Promises規范中,也沒有取消(中斷)Promise對象執行的概念,我們必須要確保Promise最終進入resolve or reject狀態之一。所以,后面的Promise對象還是會繼續執行的。
四、ES6 Promise基本用法 1. 創建Promise對象。new Promise(fn) 返回一個Promise對象
在 fn 中指定異步等處理。
處理結果正常的話,調用 resolve(處理結果值)。
處理結果錯誤的話,調用 reject(Error對象)。
//示例 function getURL(URL) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", URL); xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); } // 運行示例 var URL = "http://baidu.com"; getURL(URL) .then(function onFulfilled(value){ console.log(value); }) .catch(function onRejected(error){ console.error(error); }); // 其實 .catch 只是 Promise.then(undefined, onRejected) 的別名而已, // 如下代碼也可以完 成同樣的功能。 getURL(URL).then(onFulfilled, onRejected); getURL(URL).then(function(value) { // fulfillment }, function(reason) { // rejection });
總結:
用 new Promise 方法創建promise對象
用 .then 或 .catch 添加promise對象的處理函數
它的作用是為Promise實例添加狀態改變時的回調函數。前面說過,then方法的第一個參數是Resolved狀態的回調函數,第二個參數(可選)是Rejected狀態的回調函數。
then方法返回的是一個新的Promise實例。因此可以采用鏈式寫法,即then方法后面再調用另一個then方法。
3. Promise.prototype.catch()Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發生錯誤時的回調函數。
getAjax("url/info").then(function(data) { // ... }).catch(function(error) { // 處理 ajax 和 前一個回調函數運行時發生的錯誤 console.log("發生錯誤!", error); });
總結:
1.上面代碼中,getAjax方法返回一個 Promise 對象,如果該對象狀態變為Resolved,則會調用then方法指定的回調函數;如果異步操作拋出錯誤,狀態就會變為Rejected,就會調用catch方法指定的回調函數,處理這個錯誤。另外,then方法指定的回調函數,如果運行中拋出錯誤,也會被catch方法捕獲。
2.Promise 對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。
有了then里面的第二個onRejected函數捕獲錯誤,為什么還需要catch?
function throwError(value) { // 拋出異常 throw new Error(value); } // <1> onRejected不會被調用 function main1(onRejected) { return Promise.resolve(1).then(throwError, onRejected); } // <2> 有異常發生時onRejected會被調用 function main2(onRejected) { return Promise.resolve(1).then(throwError).catch(onRejected); } // 執行main函數 main1(function(){ console.log("錯誤異常"); } // 執行main2函數 main2(function(){ console.log("錯誤異常"); } /*Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發生錯誤時的回調函數。 一般來說,不要在then方法里面定義Reject狀態的回調函數(即then的第二個參數),總是使用catch方法。 */ Promise.resolve(1).then(throwError).then(null, onRejected);
在函數main1因為雖然我們在的第二個參數中指定了用來錯誤處理的函數,但實際上它卻不能捕獲第一個參數指定的函數(本例為throwError)里面出現的錯誤。
與此相對的是main2中的代碼則遵循了 throwError → onRejected 的調用流程。這時候出現異常的話,在會被方法鏈中的下一個方法,即 .catch 所捕獲,進行相應的錯誤處理。
總結:
.then 方法中的onRejected參數所指定的回調函數,實際上針對的是其Promise對象或者之前的Promise對象,而不是針對方法里面指定的第一個參數,即onFulfilled所指向的對象,這也是then和 catch表現不同的原因。
添加一個事件處理回調于當前promise對象,并且在原promise對象解析完畢后,返回一個新的promise對象。回調會在當前promise運行完畢后被調用,無論當前promise的狀態是完成(fulfilled)還是失敗(rejected)
5. Promise.resolve()有時需要將現有對象轉為Promise對象,Promise.resolve方法就起到這個作用。
該函數的參數四種情況:
(1)參數是一個Promise實例,那么Promise.resolve將不做任何操作,原封不動的將實例返回。
(2)參數是一個thenable對象,會將其轉為Promise對象,然后立即執行該對象的then方法。
(3)參數不是具有then方法的對象,或根本就不是對象。比如說字符之類,則Promise.resolve方法返回一個新的Promise對象,并且狀態Resolved。
(4)不帶有任何參數,直接返回一個狀態為Resolved的Promise對象。
使用Promise.resolve()創建Promise對象
// 靜態方法 Promise.resolve(value) 可以認為是 new Promise() 方法的快捷方式。 // 比如 Promise.resolve(1) .then(function(value){ console.log(value); }); // 可以認為是以下代碼的語法糖。 new Promise(function(resolve){ resolve(1); }) .then(function(value){ console.log(value); }); // 控制臺輸出1 注意:無論Promise.resolve的參數是什么,只要變成了rejected或者resolved。都會執行then里面的resolve函數。
resolve另一個promise對象
var original = Promise.resolve("我在第二行"); var cast = Promise.resolve(original); cast.then(function(value) { console.log("value: " + value); }); console.log("original === cast ? " + (original === cast)); /* * 打印順序如下,這里有一個同步異步先后執行的區別 * original === cast ? true * value: 我在第二行 */
將 thenable 對象轉換為promise對象。 什么是thenable對象?
簡單來說它就是一個非常類似promise的東西。thenable指的是一個
具有 .then 方法的對象。jQuery.ajax(),這個對象具有 .then 方法。
// Resolve一個thenable對象 var p1 = Promise.resolve({ then: function(onFulfill, onReject) { onFulfill("fulfilled!"); } }); console.log(p1 instanceof Promise) // true, 這是一個Promise對象 p1.then(function(v) { console.log(v); // 輸出"fulfilled!" }, function(e) { // 不會被調用 }); // Thenable在callback之前拋出異常 // Promise rejects var thenable = { then: function(resolve) { throw new TypeError("Throwing"); resolve("Resolving"); }}; var p2 = Promise.resolve(thenable); p2.then(function(v) { // 不會被調用 }, function(e) { console.log(e); // TypeError: Throwing }); // Thenable在callback之后拋出異常 // Promise resolves var thenable = { then: function(resolve) { resolve("Resolving"); throw new TypeError("Throwing"); }}; var p3 = Promise.resolve(thenable); p3.then(function(v) { console.log(v); // 輸出"Resolving" }, function(e) { // 不會被調用 });6. Promise.reject()
Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態為rejected。
Promise.reject("Testing static reject").then(function(reason) { // 未被調用 }, function(reason) { console.log(reason); // "Testing static reject" }); Promise.reject(new Error("fail")).then(function(result) { // 未被調用 }, function(error) { console.log(error); // stacktrace }); // 注意:無論Promise.reject的參數是什么,只要變成了rejected或者resolved。都會執行then里面的reject函數。7. Promise.all(iterable)
Promise.all(iterable) 方法返回一個 Promise 實例,此實例在 iterable 參數內所有的 promise 都“完成(resolved)”或參數中不包含 promise 時回調完成(resolve);如果參數中 promise 有一個失敗(rejected),此實例回調失敗(reject),失敗原因的是第一個失敗 promise 的結果。
var p1 = Promise.resolve(3); var p2 = 1337; var p3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, "foo"); }); Promise.all([p1, p2, p3]).then(values => { console.log(values); // [3, 1337, "foo"] });
Promise.race(iterable)
race 函數返回一個 Promise,它將與第一個傳遞的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失敗(rejects),這要取決于第一個完成的方式是兩個中的哪個。
如果傳的迭代是空的,則返回的 promise 將永遠等待。
如果迭代包含一個或多個非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析為迭代中找到的第一個值。
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); }); var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two"); }); Promise.race([p1, p2]).then(function(value) { console.log(value); // "two" // 兩個都完成,但 p2 更快 }); var p3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "three"); }); var p4 = new Promise(function(resolve, reject) { setTimeout(reject, 500, "four"); }); Promise.race([p3, p4]).then(function(value) { console.log(value); // "three" // p3 更快,所以它完成了 }, function(reason) { // 未被調用 }); var p5 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "five"); }); var p6 = new Promise(function(resolve, reject) { setTimeout(reject, 100, "six"); }); Promise.race([p5, p6]).then(function(value) { // 未被調用 }, function(reason) { console.log(reason); // "six" // p6 更快,所以它失敗了 });五、Promise只能進行異步操作?
Promise在規范上規定Promise只能使用異步調用方式。 ``` // 可以看出promise是 一個異步函數 var promise = new Promise(function(resolve) { console.log("inner promise"); // 1 resolve(42); }); promise.then(function(value) { console.log(value); // 3 }); console.log("outer promise"); // 2 ```why?
因為同步調用和異步調用同時存在容易導致一些混亂。舉個類似的例子。
function onReady(fn) { var readyState = document.readyState; if (readyState === "interactive" || readyState === "complete") { // 已加載,文檔與用戶可以開始交互 || 載入完成 fn(); } else { window.addEventListener("DOMContentLoaded", fn); } } onReady(function () { console.log("DOM fully loaded and parsed"); }); console.log("==Starting==");
如上js函數會根據執行時DOM是否已經裝載完畢來決定是對回調函數進行同步調用還是異步調用。因此,如果這段代碼在源文件中出現的位置不同,在控制臺上打印的log消息順序也會不同。為了解決這個問題,我們可以選擇統一使用異步調用的方式。
function onReadyPromise() { return new Promise(function (resolve, reject) { var readyState = document.readyState; if (readyState === "interactive" || readyState === "complete") { // 已加載,文檔與用戶可以開始交互 || 載入完成 resolve(); } else { window.addEventListener("DOMContentLoaded", resolve); } }); } onReadyPromise().then(function () { console.log("DOM fully loaded and parsed"); }); console.log("==Starting==");
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102416.html
摘要:對象表示異步操作的最終完成或失敗及其結果值。狀態初始狀態,未完成或拒絕。返回使用給定值解析的對象。根據的屬性選擇返回對應的狀態簡簡單單的敘述下常用的幾個屬性,有不對的地方請指教昨天看了一篇文章,還是挺有啟發的。。。。。 Promise The Promise object represents the eventual completion (or failure) of an asy...
閱讀 3196·2021-11-18 10:02
閱讀 1446·2021-10-12 10:08
閱讀 1239·2021-10-11 10:58
閱讀 1269·2021-10-11 10:57
閱讀 1167·2021-10-08 10:04
閱讀 2121·2021-09-29 09:35
閱讀 773·2021-09-22 15:44
閱讀 1269·2021-09-03 10:30