摘要:建立對象用來傳遞異步操作消息,代表一個未來才會知道結果的事件,并且對不同事件提供統一的以便進一步處理。此時第一個被的的返回值作為新的對象的回調函數參數偽代碼由于沒有正確的由于沒有正確的將多個對象合并成一個新的實例。
Promise 建立
Promise 對象用來傳遞異步操作消息,代表一個未來才會知道結果的事件,并且對不同事件提供統一的 API 以便進一步處理。Promise 具有以下特點:
由異步操作結果決定改狀態,其他操作絕不影響該狀態;
對象狀態不受外界影響:Promise 代表的異步操作有三個狀態:
Pending: 進行中
Resolved: 已完成(Fulfilled)
Rejected: 已失敗
一旦狀態改變,就不會再變:Promise 的狀態只有2種可能:
從 Pending 到 Resolved
從 Pending 到 Rejected
對于同一個 promise, 當以上狀態發生一個(只能發生其一),就不會再改變了。之后任何時間你都能得到這個狀態,且永不改變。
有了 Promise 就可以將層層的回調寫為同步的樣子,表示起來更清晰。不過需要注意以下幾點:
Promise 一旦建立就立即執行,并且無法中斷或取消
如果沒有設置回調函數,那么 Promise 中的產生的錯誤不會拋到外部
Pending 狀態時,我們無法知道其具體進度
Promise 的基本結構如下:
var promise = new Promise(function(resolve, reject){ if(/*異步操作成功*/){ resolve(value); } else { reject(error); } });
構造函數接受一個回調函數為參數,回調函數具有2個參數,也都是函數,resolve 在 Promise 狀態變為 resolved 時調用,reject 在 Promise 狀態變為 rejected 時調用。resolve 的接受一個參數——值或另一個 promise 對象; rejectj接受一個參數——錯誤。需要說明的是,這里的 resole 和 reject 函數已經由系統部署好了,我們可以不寫。
promise 構建好以后我們就可以調用它的then()方法,then(resolve(value){},reject(value){})方法接受2個函數參數,resolve 在 Promise 狀態變為 resolved 時調用,reject 在 Promise 狀態變為 rejected 時調用。其中 reject 參數是可選的。和構造函數不同的是,then 方法的 reject 和 resolve 都使用 promise 傳出的值作為其唯一的參數。
這里寫一個簡單的例子,理解一下:
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 實現 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 的參數是一個promise:
var p1 = new Promise(function(resolve, reject){ //... }); var p2 = new Promise(function(resolve, reject){ //... resolve(p1); });
上面代碼中 p1 的狀態傳給了 p2,也就是p1運行完成(狀態為 resolve 或 reject)后 p2 的回調函數會立刻開始執行:
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 準備執行 resolve, 但是 resolve 的參數是 p1, 此時 p1 還是 Pending 狀態,所以 p2 開始等待。又過了 2s, p1 的 reject 執行,變為 rejected 狀態,隨即 p2 也跟著變成 rejected 狀態。
Promise 對象方法then() 方法
then(resolve(value){},reject(value){})方法接受2個函數參數,resolve 在 Promise 狀態變為 resolved 時調用,reject 在 Promise 狀態變為 rejected 時調用。其中 reject 參數是可選的。和構造函數不同的是,then 方法的 reject 和 resolve 都使用 promise 傳出的值作為其唯一的參數。
then() 方法返回一個新的 Promise 實例,注意,不是之前那個。因此可以用鏈式調用,不斷添加"回調"函數。 then 的返回值成了下一個 then 中回調函數的參數:
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 的狀態是 resolved 則執行 then參數中的第一個回調函數;如果 promise 的狀態是 rejected 則執行 then參數中的第二個回調函數。這個狀態是不斷傳遞下來的,這一點和之前的例子類似。
catch() 方法:
catch(reject) 方法是 then(null, reject) 的別名,在發生錯誤的時候執行其參數函數:
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 });
錯誤會從最初的請求沿著回調函數,一直被傳遞下來。這一點和傳統的錯誤冒泡類似,無論哪里有錯誤都可以被捕獲到:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不執行 throw new Error("man-made Error"); }).catch(function(err){ console.log(err.message); //original Error });
當然也可以在半路截住錯誤:
new Promise(function(resolve, reject){ reject(new Error("original Error")); }).then(function(val){ console.log(val); //不執行 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); //不執行 });
這里需要注意以下幾點:
reject 和 throw 一樣可以拋出錯誤。
在 Promise 狀態變為 resolved 或 rejected 之后拋出的錯誤會被忽略。
建議總是使用 catch() 方法,而不要在 then() 方法中定義 reject 函數。
如果一個 promise 既沒有 catch方法,也沒有可以捕獲到錯誤的 then 方法,那么這個錯誤就消失了。它不會到 promise 外面來。
try...catch... 只能捕獲同步代碼的錯誤,不能捕獲異步代碼的錯誤(這個是 ES5 就有的)。
catch() 方法可以繼續拋出錯誤,就像 try...catch 中的 catch 一樣可以拋出錯誤。
這里需要說明的是第4條:錯誤不會到 Promise 外面是 ES6 規范的說法。具體理解(瀏覽器環境):控制臺依舊會報錯,但是不影響 promise 語句之后續代碼執行。此外,promise 語句內的異步語句(如事件,定時器等等)拋出的錯誤,不屬于 promise 內部,發生錯誤會傳播出去:
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 對象,可以繼續調用 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 不能捕獲在其后面的語句中出現的錯誤:
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() 接受一個回調函數(無參數)為參數,和 try...catch...finally 中的 finally 類似,不論 promise 是什么狀態,該回調函數都一定會運行??梢杂盟P閉文件,或者關閉服務器等:
server.listen(0).then(function(){ //do sth. }).finally(server.stop);
finally() 內部實現如下:
Promise.prototype.finally = function(callback){ return this.then( value => {Promise.resolve(callback()).then(() => value)}, error => {Promise.resolve(callback()).then(() => {throw error})} ); };
done() 方法
done() 方法用在 promise 處理語句的末端,用來處理可能未捕獲的錯誤,并拋向全局。如果其帶有參數,可以等效為 done() 之前多了一個 then():
p.done(fun1, fun2); //相當于 p.then(fun1,fun2).done();
done() 內部實現如下:
Promise.prototype.done = function(onResolve, onRejected){ this.then(onResolve, onRejected).catch(function(err){ setTimeout(() => {throw err}, 0); }); };Promise 靜態方法
Promise.all()
將多個 promise 對象合并成一個新的 promise 實例。其接受一個裝僅有 promise 對象的可遍歷結構為參數,如果不是 promise 對象,系統會調用 Promise.resolve() 進行類型轉換。
promise.all() 方法得到的新的 promise 對象狀態由構成它的所有 promise 對象決定,具體分為2種情況:
當所有構成它的 promise 對象的狀態都變成 resolved,這個新的對象狀態才變為 resolved。此時構成它所有的 Promise 的返回值構成一個數組作為新的 promise 對象的回調函數參數;
當所有構成它的 promise 對象的狀態有一個變成 rejected,這個新的對象狀態就變為 rejected。此時第一個被 reject 的 Promise 的返回值作為新的 promise 對象的回調函數參數;
//偽代碼, 由于沒有正確的 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 對象的可遍歷結構為參數,如果不是 promise 對象,系統會調用 Promise.resolve() 進行類型轉換。
和 promise.all() 不同的是 Promise.race() 方法得到的新的 promise 對象狀態由構成它的 promise 對象中最先改變狀態的那一個決定。
//偽代碼, 由于沒有正確的 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不能獲得數據就報錯 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()
將現有對象轉化為 promise 對象:
var p = Promise.resolve($.ajax("url")); //jQuery的 $.ajax 方法 //等同于: var p = new Promise(function(resolve){ resolve($.ajax("url")); });
如果傳入 Promise.resolve() 的對象不具有 then 方法(ie. unthenable), 則返回一個狀態為 resolved 的新 promise 對象。
Promise.resolve("hello").then(function(val){ console.log(val); //hello });
如果你僅僅想得到一個 promise 對象,那利用 resolve() 方法是最簡單的:
var promise = Promise.resolve();
Promise.reject()
Promise.reject(reason), 返回一個狀態為 rejected 的 promise 實例。參數 reason 會被傳遞被實例的回調函數。
Promise.reject(new Error("error occured")).catch(err => console.log(err.message)); //error occured應用舉例
加載圖片:
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 函數
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); })();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97442.html
摘要:異步編程程序執行分為同步和異步,如果程序每執行一步都需要等待上一步完成才能開始,此所謂同步。因此異步編程十分重要。 異步編程 程序執行分為同步和異步,如果程序每執行一步都需要等待上一步完成才能開始,此所謂同步。如果程序在執行一段代碼的同時可以去執行另一段代碼,等到這段代碼執行完畢再吧結果交給另一段代碼,此所謂異步。比如我們需要請求一個網絡資源,由于網速比較慢,同步編程就意味著用戶必須等...
摘要:允許我們把水平的代碼回調函數的地獄轉換為豎直的代碼在之前,我們使用或是,現在我們有了這里我們有個,執行成功時調用的函數和失敗時調用的函數。使用的好處使用嵌套的回調函數處理錯誤會很混亂。 es6-參考手冊 該手冊包括ES2015[ES6]的知識點、技巧、建議和每天工作用的代碼段例子。歡迎補充和建議。 var 和 let / const 除了var,我們現在有了兩種新的標示符用來存儲值——...
摘要:基本類型是一種解決命名沖突的工具。這樣,就有了個基本類型和個復雜類型使用需要注意以下幾點和一樣不具有構造函數,不能用調用。判斷對象是否某個構造函數的實例,運算符會調用它是一個數組對象屬性。即,當存在時,以此為構造函數構建對象。 Symbol基本類型 Symbol 是一種解決命名沖突的工具。試想我們以前定義一個對象方法的時候總是要檢查是否已存在同名變量: if(String && Str...
摘要:由于中引入了許多數據結構算上原有的包括等等數組需要一個東西來管理他們這就是遍歷器。數組默認遍歷器遍歷值相當于依次輸出依次輸出依次輸出依次輸出不難看出默認得到值而只能得到索引。即遍歷器的本質就是一個指針。 由于 ES6 中引入了許多數據結構, 算上原有的包括Object, Array, TypedArray, DataView, buffer, Map, WeakMap, Set, We...
摘要:對象是工作組為異步編程提供的統一接口,是中提供了對的原生支持,就是在未來發生的事情,使用可以避免回調函數的層層嵌套,還提供了規范更加容易的對異步操作進行控制。是執行完之后的回調,可以用方法分別指定和的回調。 Promise對象是CommonJS工作組為異步編程提供的統一接口,是ECMAScript6中提供了對Promise的原生支持,Promise就是在未來發生的事情,使用Promis...
閱讀 2318·2023-04-25 14:17
閱讀 1521·2021-11-23 10:02
閱讀 2175·2021-11-23 09:51
閱讀 880·2021-10-14 09:49
閱讀 3389·2021-10-11 10:57
閱讀 2927·2021-09-24 09:47
閱讀 3051·2021-08-24 10:00
閱讀 2303·2019-08-29 18:46