摘要:建立對象用來傳遞異步操作消息,代表一個未來才會知道結(jié)果的事件,并且對不同事件提供統(tǒng)一的以便進一步處理。此時第一個被的的返回值作為新的對象的回調(diào)函數(shù)參數(shù)偽代碼由于沒有正確的由于沒有正確的將多個對象合并成一個新的實例。
Promise 建立
Promise 對象用來傳遞異步操作消息,代表一個未來才會知道結(jié)果的事件,并且對不同事件提供統(tǒng)一的 API 以便進一步處理。Promise 具有以下特點:
由異步操作結(jié)果決定改狀態(tài),其他操作絕不影響該狀態(tài);
對象狀態(tài)不受外界影響:Promise 代表的異步操作有三個狀態(tài):
Pending: 進行中
Resolved: 已完成(Fulfilled)
Rejected: 已失敗
一旦狀態(tài)改變,就不會再變:Promise 的狀態(tài)只有2種可能:
從 Pending 到 Resolved
從 Pending 到 Rejected
對于同一個 promise, 當(dāng)以上狀態(tài)發(fā)生一個(只能發(fā)生其一),就不會再改變了。之后任何時間你都能得到這個狀態(tài),且永不改變。
有了 Promise 就可以將層層的回調(diào)寫為同步的樣子,表示起來更清晰。不過需要注意以下幾點:
Promise 一旦建立就立即執(zhí)行,并且無法中斷或取消
如果沒有設(shè)置回調(diào)函數(shù),那么 Promise 中的產(chǎn)生的錯誤不會拋到外部
Pending 狀態(tài)時,我們無法知道其具體進度
Promise 的基本結(jié)構(gòu)如下:
var promise = new Promise(function(resolve, reject){ if(/*異步操作成功*/){ resolve(value); } else { reject(error); } });
構(gòu)造函數(shù)接受一個回調(diào)函數(shù)為參數(shù),回調(diào)函數(shù)具有2個參數(shù),也都是函數(shù),resolve 在 Promise 狀態(tài)變?yōu)?resolved 時調(diào)用,reject 在 Promise 狀態(tài)變?yōu)?rejected 時調(diào)用。resolve 的接受一個參數(shù)——值或另一個 promise 對象; rejectj接受一個參數(shù)——錯誤。需要說明的是,這里的 resole 和 reject 函數(shù)已經(jīng)由系統(tǒng)部署好了,我們可以不寫。
promise 構(gòu)建好以后我們就可以調(diào)用它的then()方法,then(resolve(value){},reject(value){})方法接受2個函數(shù)參數(shù),resolve 在 Promise 狀態(tài)變?yōu)?resolved 時調(diào)用,reject 在 Promise 狀態(tài)變?yōu)?rejected 時調(diào)用。其中 reject 參數(shù)是可選的。和構(gòu)造函數(shù)不同的是,then 方法的 reject 和 resolve 都使用 promise 傳出的值作為其唯一的參數(shù)。
這里寫一個簡單的例子,理解一下:
function timeout(ms){ return new Promise((resolve, reject) => { console.log("promise"); //"promise" setTimeout(resolve, ms, "done"); }); } timeout(2000).then((value) => { console.log(value); //2秒后得到 "done" });
利用 Promise 異步加載圖片:
function loadImageAsync(url){ return new Promise(function(resole, reject){ var image = new Image(); image.onload = function(){ resolve(image); }; image.onerror = function(){ reject(new Error(`Could not load image at ${url}`)); }; image.src = url; }); }
利用 Promise 實現(xiàn) Ajax:
var id = document.getElementById("primary"); var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } getJSON("info.json").then( json => id.innerHTML = "" + json + "", err => id.innerHTML = err );
如果 resolve 的參數(shù)是一個promise:
var p1 = new Promise(function(resolve, reject){ //... }); var p2 = new Promise(function(resolve, reject){ //... resolve(p1); });
上面代碼中 p1 的狀態(tài)傳給了 p2,也就是p1運行完成(狀態(tài)為 resolve 或 reject)后 p2 的回調(diào)函數(shù)會立刻開始執(zhí)行:
var p1 = new Promise(function(resolve, reject){ setTimeout(() => reject(new Error("failed")), 3000); }); var p2 = new Promise(function(resolve, reject){ setTimeout(() => resolve(p1), 1000); }); p2.then(result => console.log(result)); p2.catch(error => console.log(error));
p1 建立,進入 setTimeout 異步計時器。之后 p2 建立,進入 setTimeout 異步計時器。1s 后 p2 準(zhǔn)備執(zhí)行 resolve, 但是 resolve 的參數(shù)是 p1, 此時 p1 還是 Pending 狀態(tài),所以 p2 開始等待。又過了 2s, p1 的 reject 執(zhí)行,變?yōu)?rejected 狀態(tài),隨即 p2 也跟著變成 rejected 狀態(tài)。
Promise 對象方法then() 方法
then(resolve(value){},reject(value){})方法接受2個函數(shù)參數(shù),resolve 在 Promise 狀態(tài)變?yōu)?resolved 時調(diào)用,reject 在 Promise 狀態(tài)變?yōu)?rejected 時調(diào)用。其中 reject 參數(shù)是可選的。和構(gòu)造函數(shù)不同的是,then 方法的 reject 和 resolve 都使用 promise 傳出的值作為其唯一的參數(shù)。
then() 方法返回一個新的 Promise 實例,注意,不是之前那個。因此可以用鏈?zhǔn)秸{(diào)用,不斷添加"回調(diào)"函數(shù)。 then 的返回值成了下一個 then 中回調(diào)函數(shù)的參數(shù):
var p = new Promise(function(resolve, reject){ resolve("from new Promise"); }).then(function (value){ console.log(value); //from new Promise 其次輸出這個 return "from the first "then""; }).then(function(value){ console.log(value); //from the first "then" 最后輸出這個 return "from the second "then""; }); console.log(p); //Promise{...} 先輸出這個
注意,如果 promise 的狀態(tài)是 resolved 則執(zhí)行 then參數(shù)中的第一個回調(diào)函數(shù);如果 promise 的狀態(tài)是 rejected 則執(zhí)行 then參數(shù)中的第二個回調(diào)函數(shù)。這個狀態(tài)是不斷傳遞下來的,這一點和之前的例子類似。
catch() 方法:
catch(reject) 方法是 then(null, reject) 的別名,在發(fā)生錯誤的時候執(zhí)行其參數(shù)函數(shù):
new Promise(function(resolve, reject){ resolve("resolved"); }).then(function(val){ console.log(val); //resolved throw new Error("man-made Error"); }).catch(function(err){ console.log(err.message); //man-made Error });
錯誤會從最初的請求沿著回調(diào)函數(shù),一直被傳遞下來。這一點和傳統(tǒng)的錯誤冒泡類似,無論哪里有錯誤都可以被捕獲到:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不執(zhí)行 throw new Error("man-made Error"); }).catch(function(err){ console.log(err.message); //original Error });
當(dāng)然也可以在半路截住錯誤:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不執(zhí)行 throw new Error("man-made Error"); }, function(err){ console.log(`Uncaught Error: ${err.message}`); //Uncaught Error: original Error }).catch(function(err){ console.log(err.message); //不執(zhí)行 });
這里需要注意以下幾點:
reject 和 throw 一樣可以拋出錯誤。
在 Promise 狀態(tài)變?yōu)?resolved 或 rejected 之后拋出的錯誤會被忽略。
建議總是使用 catch() 方法,而不要在 then() 方法中定義 reject 函數(shù)。
如果一個 promise 既沒有 catch方法,也沒有可以捕獲到錯誤的 then 方法,那么這個錯誤就消失了。它不會到 promise 外面來。
try...catch... 只能捕獲同步代碼的錯誤,不能捕獲異步代碼的錯誤(這個是 ES5 就有的)。
catch() 方法可以繼續(xù)拋出錯誤,就像 try...catch 中的 catch 一樣可以拋出錯誤。
這里需要說明的是第4條:錯誤不會到 Promise 外面是 ES6 規(guī)范的說法。具體理解(瀏覽器環(huán)境):控制臺依舊會報錯,但是不影響 promise 語句之后續(xù)代碼執(zhí)行。此外,promise 語句內(nèi)的異步語句(如事件,定時器等等)拋出的錯誤,不屬于 promise 內(nèi)部,發(fā)生錯誤會傳播出去:
var p = new Promise(function(resolve, reject){ resolve("ok"); setTimeout(function(){throw new Error("setTimeout error")},0); }); p.then(function(val){console.log(val);}); //ok //Uncaught Error: setTimeout error
其次,就以上前兩個注意事項舉一例說明:
new Promise(function(resolve, reject){ resolve("resolved"); throw "original Error"; //被忽略 }).then(function(val){ console.log(val); //resolved throw (new Error("man-made Error")); }).catch(function(err){ console.log(err.message); //man-made Error });
catch 方法的返回值還是一個新的 promise 對象,可以繼續(xù)調(diào)用 then 等其他方法:
new Promise(function(resolve, reject){ reject(new Error("reject")); }).catch(function(err){ console.log("1st catch"); //被跳過 return "continue"; }).then(function(val){ console.log(val); //continue });
如果 catch之前沒有錯誤,該 catch 會被跳過。這意味著,catch 不能捕獲在其后面的語句中出現(xiàn)的錯誤:
new Promise(function(resolve, reject){ resolve("resolved"); }).catch(function(err){ console.log("1st catch"); //被跳過 }).then(function(val){ console.log(val); //resolved throw (new Error()); }).catch(function(err){ console.log("2nd catch"); //2nd catch });
finally() 方法
finally() 接受一個回調(diào)函數(shù)(無參數(shù))為參數(shù),和 try...catch...finally 中的 finally 類似,不論 promise 是什么狀態(tài),該回調(diào)函數(shù)都一定會運行??梢杂盟P(guān)閉文件,或者關(guān)閉服務(wù)器等:
server.listen(0).then(function(){ //do sth. }).finally(server.stop);
finally() 內(nèi)部實現(xiàn)如下:
Promise.prototype.finally = function(callback){ return this.then( value => {Promise.resolve(callback()).then(() => value)}, error => {Promise.resolve(callback()).then(() => {throw error})} ); };
done() 方法
done() 方法用在 promise 處理語句的末端,用來處理可能未捕獲的錯誤,并拋向全局。如果其帶有參數(shù),可以等效為 done() 之前多了一個 then():
p.done(fun1, fun2); //相當(dāng)于 p.then(fun1,fun2).done();
done() 內(nèi)部實現(xiàn)如下:
Promise.prototype.done = function(onResolve, onRejected){ this.then(onResolve, onRejected).catch(function(err){ setTimeout(() => {throw err}, 0); }); };Promise 靜態(tài)方法
Promise.all()
將多個 promise 對象合并成一個新的 promise 實例。其接受一個裝僅有 promise 對象的可遍歷結(jié)構(gòu)為參數(shù),如果不是 promise 對象,系統(tǒng)會調(diào)用 Promise.resolve() 進行類型轉(zhuǎn)換。
promise.all() 方法得到的新的 promise 對象狀態(tài)由構(gòu)成它的所有 promise 對象決定,具體分為2種情況:
當(dāng)所有構(gòu)成它的 promise 對象的狀態(tài)都變成 resolved,這個新的對象狀態(tài)才變?yōu)?resolved。此時構(gòu)成它所有的 Promise 的返回值構(gòu)成一個數(shù)組作為新的 promise 對象的回調(diào)函數(shù)參數(shù);
當(dāng)所有構(gòu)成它的 promise 對象的狀態(tài)有一個變成 rejected,這個新的對象狀態(tài)就變?yōu)?rejected。此時第一個被 reject 的 Promise 的返回值作為新的 promise 對象的回調(diào)函數(shù)參數(shù);
//偽代碼, 由于沒有正確的 url var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } var pros = ["url1", "url2", "url3"].map(url => getJSON(url)); Promise.all(pros).then(function(){ console.log("all successful"); }, function(){ console.log("one rejected"); //one rejected, 由于沒有正確的 url });
Promise.race()
將多個 promise 對象合并成一個新的 promise 實例。其接受一個裝僅有 promise 對象的可遍歷結(jié)構(gòu)為參數(shù),如果不是 promise 對象,系統(tǒng)會調(diào)用 Promise.resolve() 進行類型轉(zhuǎn)換。
和 promise.all() 不同的是 Promise.race() 方法得到的新的 promise 對象狀態(tài)由構(gòu)成它的 promise 對象中最先改變狀態(tài)的那一個決定。
//偽代碼, 由于沒有正確的 url var getJSON = function(url){ var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.response = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler(){ if(client.readyState !== 4) return; if(this.status === 200){ resolve(client.response); } else { reject(new Error(this.statusText)); } } }); return promise; } //如果5s不能獲得數(shù)據(jù)就報錯 var p = Promise.race([ getJSON("url"), new Promise(function(resolve, reject){ setTimeout(() => reject(new Error("Timeout")), 5000); }) ]).then(res => console.log(res)) .catch(err => console.log(err)); //Error, 由于沒有正確的 url
Promise.resolve()
將現(xiàn)有對象轉(zhuǎn)化為 promise 對象:
var p = Promise.resolve($.ajax("url")); //jQuery的 $.ajax 方法 //等同于: var p = new Promise(function(resolve){ resolve($.ajax("url")); });
如果傳入 Promise.resolve() 的對象不具有 then 方法(ie. unthenable), 則返回一個狀態(tài)為 resolved 的新 promise 對象。
Promise.resolve("hello").then(function(val){ console.log(val); //hello });
如果你僅僅想得到一個 promise 對象,那利用 resolve() 方法是最簡單的:
var promise = Promise.resolve();
Promise.reject()
Promise.reject(reason), 返回一個狀態(tài)為 rejected 的 promise 實例。參數(shù) reason 會被傳遞被實例的回調(diào)函數(shù)。
Promise.reject(new Error("error occured")).catch(err => console.log(err.message)); //error occured應(yīng)用舉例
加載圖片:
var preloadImage = function(url){ return new Promise(function(resolve, reject){ var image = new Image(); image.onload = resolve; image.onerror = reject; image.src = url; }); };
使用 Generator 管理流程,用 promise 進行異步操作
function getFoo(){ return new Promise(function(resolve){ resolve("foo"); }); } function* gen(){ try{ var foo = yield getFoo(); console.log(foo); } catch(e) { console.log(e); } } var it = gen(); (function go(result){ if(result.done) return result.value; return result.value.then(function(value){ return go(it.next(value)); }, function(err){ return go(it.throw(error)); }); })(it.next()); //foo
異步中模擬 sleep 函數(shù)
const sleep = (time) => new Promise(function(resolve){ setTimeout(resolve, time); }); (async () => { for(var i = 0; i < 5; i++){ await sleep(1000); console.log(new Date, i); } await sleep(1000); console.log(new Date, i); })();
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97442.html
摘要:異步編程程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開始,此所謂同步。因此異步編程十分重要。 異步編程 程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開始,此所謂同步。如果程序在執(zhí)行一段代碼的同時可以去執(zhí)行另一段代碼,等到這段代碼執(zhí)行完畢再吧結(jié)果交給另一段代碼,此所謂異步。比如我們需要請求一個網(wǎng)絡(luò)資源,由于網(wǎng)速比較慢,同步編程就意味著用戶必須等...
摘要:允許我們把水平的代碼回調(diào)函數(shù)的地獄轉(zhuǎn)換為豎直的代碼在之前,我們使用或是,現(xiàn)在我們有了這里我們有個,執(zhí)行成功時調(diào)用的函數(shù)和失敗時調(diào)用的函數(shù)。使用的好處使用嵌套的回調(diào)函數(shù)處理錯誤會很混亂。 es6-參考手冊 該手冊包括ES2015[ES6]的知識點、技巧、建議和每天工作用的代碼段例子。歡迎補充和建議。 var 和 let / const 除了var,我們現(xiàn)在有了兩種新的標(biāo)示符用來存儲值——...
摘要:基本類型是一種解決命名沖突的工具。這樣,就有了個基本類型和個復(fù)雜類型使用需要注意以下幾點和一樣不具有構(gòu)造函數(shù),不能用調(diào)用。判斷對象是否某個構(gòu)造函數(shù)的實例,運算符會調(diào)用它是一個數(shù)組對象屬性。即,當(dāng)存在時,以此為構(gòu)造函數(shù)構(gòu)建對象。 Symbol基本類型 Symbol 是一種解決命名沖突的工具。試想我們以前定義一個對象方法的時候總是要檢查是否已存在同名變量: if(String && Str...
摘要:由于中引入了許多數(shù)據(jù)結(jié)構(gòu)算上原有的包括等等數(shù)組需要一個東西來管理他們這就是遍歷器。數(shù)組默認遍歷器遍歷值相當(dāng)于依次輸出依次輸出依次輸出依次輸出不難看出默認得到值而只能得到索引。即遍歷器的本質(zhì)就是一個指針。 由于 ES6 中引入了許多數(shù)據(jù)結(jié)構(gòu), 算上原有的包括Object, Array, TypedArray, DataView, buffer, Map, WeakMap, Set, We...
摘要:對象是工作組為異步編程提供的統(tǒng)一接口,是中提供了對的原生支持,就是在未來發(fā)生的事情,使用可以避免回調(diào)函數(shù)的層層嵌套,還提供了規(guī)范更加容易的對異步操作進行控制。是執(zhí)行完之后的回調(diào),可以用方法分別指定和的回調(diào)。 Promise對象是CommonJS工作組為異步編程提供的統(tǒng)一接口,是ECMAScript6中提供了對Promise的原生支持,Promise就是在未來發(fā)生的事情,使用Promis...
閱讀 2344·2023-04-25 14:17
閱讀 1536·2021-11-23 10:02
閱讀 2186·2021-11-23 09:51
閱讀 894·2021-10-14 09:49
閱讀 3400·2021-10-11 10:57
閱讀 2932·2021-09-24 09:47
閱讀 3063·2021-08-24 10:00
閱讀 2311·2019-08-29 18:46