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