前端异步对象的原理与使用方法
源寶導讀:現今互聯網的WEB網站,幾乎沒有不用到JS異步技術的,雖然大家經常用到,但Javascript提供的異步機制如何才能真正用好呢,可能很多開發小伙伴還有點含糊,本文將從常見的開發場景出發,系統的介紹JS異步對象的原理與使用方法。(正文約9000字,預計閱讀時間10分鐘)
一、promise是什么
? ? Promise 是異步編程的一種解決方案:從語法上講,promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。
二、Promise的優點
2.1、解決回調導致的代碼難以維護
? ? 在使用JavaScript時,為了實現某些邏輯經常會寫出層層嵌套的回調函數,如果嵌套過多,會極大影響代碼可讀性和邏輯,這種情況也被成為回調地獄,比如:
// 優化前 var sayhello = function (name, callback) {setTimeout(function () {console.log(name);callback();}, 1000); } sayhello("first", function () {sayhello("second", function () {sayhello("third", function () {console.log("end");});}); }); // 優化后 var sayhello = function (name) {return new Promise(function (resolve, reject) {setTimeout(function () {console.log(name);resolve(); //在異步操作執行完后執行 resolve() 函數}, 1000);}); } sayhello("first").then(function () {return sayhello("second"); //仍然返回一個 Promise 對象 }).then(function () {return sayhello("third"); }).then(function () {console.log('end'); })2.2、可讀性
? ? Promise規范了這種異步場景的代碼,相對callback的方式,更加清晰易懂,語義化更強。
2.3、可靠性
? ? Promise是原生支持的API,它已經被加到了JS的規范里面,在各大瀏覽器中的運行機制是相同的。這樣就保證了它的可靠。
2.4、信任問題
? ? 只能決議一次,決議值只能有一個,決議之后無法改變。任何then中的回調也只會被調用一次。Promise的特征保證了Promise可以解決信任問題。
三、promise的三種狀態
pending: 初始狀態,既不是成功,也不是失敗狀態。
fulfilled: 意味著操作成功完成。
rejected: 意味著操作失敗。
? ? pending 狀態的 Promise 對象可能會變為fulfilled 狀態并傳遞一個值給相應的狀態處理方法,也可能變為失敗狀態(rejected)并傳遞失敗信息。當其中任一種情況出現時,Promise 對象的 then 方法綁定的處理方法(handlers )就會被調用。
3.1、兼容性
對于低版本不支持的瀏覽器可以使用開源的polyfill解決。
[https://github.com/stefanpenner/es6-promise]
3.2、基礎使用方式
let myFirstPromise = new Promise(function(resolve, reject){//當異步代碼執行成功時,我們才會調用resolve(...), 當異步代碼失敗時就會調用reject(...)//在本例中,我們使用setTimeout(...)來模擬異步代碼,實際編碼時可能是XHR請求或是HTML5的一些API方法.setTimeout(function(){resolve("成功!"); //代碼正常執行!}, 250); });myFirstPromise.then(function(successMessage){//successMessage的值是上面調用resolve(...)方法傳入的值.//successMessage參數不一定非要是字符串類型,這里只是舉個例子console.log("Yay! " + successMessage); }, function (errorMessage) {console.log(errorMessage); });四、Promise接口
4.1、Promise.prototype.then
? ? 方法返回一個新的Promise。它最多需要有兩個參數:Promise 的成功和失敗情況的回調函數。
基礎使用方式
var p1 = new Promise((resolve, reject) => {resolve('成功!');// or// reject(new Error("出錯了!")); });p1.then(value => {console.log(value); // 成功!}, reason => {console.error(reason); // 出錯了!});返回新的Promise
? ? 每次執行then之后都將返回新的Promise對象。
回調都沒有定義,那么這個新的Promise狀態將為原 Promise的狀態
var promise = new Promise(function(resolve, reject){reject('error') }) var promise1 = promise promise1.then(function(){console.log('success') }, function (rs) {console.log(rs) }) // 輸出 // error回調中返回了新的狀態,那么以新的狀態為準
var promise2 = new Promise(function(resolve, reject){reject('error') }) var promise3 = promise2.then(null, function () {return Promise.resolve('promise3-success') }) promise3.then(function(rs){console.log(rs) }, function (rs) {// 不會執行 }) // 輸出 // promise3-success沒有定義回調,那么以原來Promise狀態為準
var promise = Promise.reject('error') promise.then(() => {// 不會執行 }, null).then(() => {// 不會執行 }, (rs) => {console.log(rs) // error }回調中如果返回了Error,那么新的Promise狀態為rejected
var promise4 = new Promise(function(resolve, reject){resolve('success') }) var promise5 = promise4.then(function(){throw new Error('error') }) promise5.then(function(){}, function(rs){console.log(rs) }) // 輸出 // errorPromise可重復執行then或者catch
var promise = new Promise(function(resolve, reject){resolve('success') }) promise.then(function(){console.log('one') }) promise.then(function(){console.log('two') }) promise.then(function(){console.log('three') }) // 輸出 // one // two // three4.2、Promise.prototype.catch
? ? 添加一個拒絕(rejection) 回調到當前 promise, 返回一個新的promise。當這個回調函數被調用,新 promise 將以它的返回值來resolve,否則如果當前promise 進入fulfilled狀態,則以當前promise的完成結果作為新promise的完成結果。
使用鏈式語句的 catch方法
var p1 = new Promise(function(resolve, reject) {resolve('Success'); }); p1.then(function(value) {console.log(value);return Promise.reject('oh, no!'); }).catch(function(e) {console.log(e); // "oh, no!" }).then(function(){console.log('after a catch the chain is restored'); }, function () {console.log('Not fired due to the catch'); });// 輸出為 // 'Success' // 'oh, no!' //?'after?a?catch?the?chain?is?restored捕拋出的錯誤
// 拋出一個錯誤,大多數時候將調用catch方法 var p1 = new Promise(function(resolve, reject) {throw 'Uh-oh!'; });p1.catch(function(e) {console.log(e); // "Uh-oh!" });// 在異步函數中拋出的錯誤不會被catch捕獲到 var p2 = new Promise(function(resolve, reject) {setTimeout(function() {throw 'Uncaught Exception!';}, 1000); });p2.catch(function(e) {console.log(e); // 不會執行 });// 在resolve()后面拋出的錯誤會被忽略 var p3 = new Promise(function(resolve, reject) {resolve();throw 'Silenced Exception!'; });p3.catch(function(e) {console.log(e); // 不會執行 });如果已解決
//創建一個新的 Promise ,且已解決 var p1 = Promise.resolve("calling next");var p2 = p1.catch(function (reason) {//這個方法永遠不會調用console.log("catch p1!");console.log(reason); });p2.then(function (value) {console.log("next promise's onFulfilled"); console.log(value); }, function (reason) {console.log("next promise's onRejected");console.log(reason); }); // 輸出 // next promise's onFulfilled // calling next4.3、Promise.prototype.allSettled
? ? 返回一個promise,該promise在所有給定的promise已被解析或被拒絕后解析,并且每個對象都描述每個promise的結果。
兼容性并不是很好,polyfill也并未支持
4.4、Promise.prototype.finally
? ? 返回一個Promise。在promise結束時,無論結果是fulfilled或者是rejected,都會執行指定的回調函數。這為在Promise是否成功完成后都需要執行的代碼提供了一種方式。
var p1 = new Promise(function(resolve, reject){resolve('success') }) var p2 = new Promise(function(resolve, reject){reject('error') })p1.finally(function(){console.log('one') }) p2.finally(function(rs){console.log('two') })// 輸出 // one // two? ? 注意:由于無法知道promise的最終狀態,所以finally的回調函數中不接收任何參數,它僅用于無論最終結果如何都要執行的情況。
4.5、Promise.all
? ? 這個方法返回一個新的promise對象,該promise對象在參數對象里所有的promise對象都成功的時候才會觸發成功,一旦有任何一個參數里面的promise對象失敗則立即觸發該promise對象的失敗。這個新的promise對象在觸發成功狀態以后,會把一個包含參數里所有promise返回值的數組作為成功回調的返回值,順序跟參數的順序保持一致;如果這個新的promise對象觸發了失敗狀態,它會把參數里第一個觸發失敗的promise對象的錯誤信息作為它的失敗錯誤信息。Promise.all方法常被用于處理多個promise對象的狀態集合。
var promise1 = Promise.resolve(3); var promise2 = 42; var promise3 = new Promise(function(resolve, reject) {setTimeout(resolve, 100, 'foo'); });Promise.all([promise1, promise2, promise3]).then(function(values) {console.log(values); }); // 輸出 // [3, 42, "foo"]4.6、Promise.reject
? ? 返回一個帶有拒絕原因reason參數的Promise對象。
Promise.reject("error").then(function(reason) {// 未被調用 }, function(reason) {console.log(reason); // "error" });Promise.reject(new Error("fail")).then(function(result) {// 未被調用 }, function(error) {console.log(error); // stacktrace });4.7、Promise.resolve
? ? 返回一個以給定值解析后的Promise 對象。如果該值為promise,返回這個promise;如果這個值是帶有”then” 方法,返回的promise會采用它的最終狀態;否則返回的promise將以此值完成。
Promise.resolve("Success").then(function(value) {console.log(value); // "Success" }, function(value) {// 不會被調用 });Resolve另一個promise
var original = Promise.resolve(33); var cast = Promise.resolve(original); cast.then(function(value) {console.log('value: ' + value); }); console.log('original === cast ? ' + (original === cast));// 輸出 // original === cast ? true // value: 33resolve 包含then的對象參數
// resolve var p1 = Promise.resolve({ then: function(onFulfill, onReject) { onFulfill("fulfilled!"); } });p1.then(function(v) {console.log(v); // 輸出"fulfilled!"}, function(e) {// 不會被調用 });// reject var p2 = Promise.resolve({ then: function(onFulfill, onReject) { onReject("rejected!"); } });p2.then(function(v) {// 不會被調用}, function(e) {console.log(e); // 輸出"fulfilled!" });4.8、Promise.race
? ? 當參數里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數調用父promise綁定的相應句柄,并返回該promise對象。
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);// Both resolve, but p2 is faster }); // 輸出 // two五、Promise事件
? ? 使用Promise編寫異步代碼時,使用reject來處理錯誤。有時,開發者通常會忽略這一點,導致一些錯誤沒有得到處理。例如:
new Promise((resolve, reject) => {reject('error') }).then(function(){}).then(function(){})? ? 由于沒有使用catch方法捕獲錯誤,當reject時,拋出的錯誤則沒有被處理。
5.1、unhandledrejection
? ? 當 Promise 被拒絕,但沒有提供 reject 函數來處理該 rejection 時,會派發此事件。
window.addEventListener('unhandledrejection', event => {console.log(event.reason); // 打印"Hello, Fundebug!" });Promise.reject('Hello, Fundebug!');5.2、rejectionhandled
? ? 當一個Promise錯誤最初未被處理,但是稍后又得到了處理,則會派發此事件:
window.addEventListener('unhandledrejection', event => {console.log(event.reason); // 打印"Hello, Fundebug!" })window.addEventListener('rejectionhandled', event => {console.log('rejection handled'); // 1秒后打印"rejection handled" });function foo() {return Promise.reject('Hello, Fundebug!'); }var r = foo();setTimeout(() =>{r.catch(e =>{}); }, 1000);? ? 我們可以通過以上事件為 Promise 失敗時提供補償處理,也有利于調試 Promise 相關的問題。在每一個上下文中,該處理都是全局的,因此不管源碼如何,所有的錯誤都會在同一個handler中被捕捉處理。
舉例如下:
window.addEventListener("unhandledrejection", event => {/* 你可以在這里添加一些代碼,以便檢查event.promise 中的 promise 和event.reason 中的 rejection 原因 */event.preventDefault(); }, false);六、高級使用方式
6.1、鏈式調用
? ? 連續執行兩個或者多個異步操作是一個常見的需求,在上一個操作執行成功之后,開始下一個的操作,并帶著上一步操作所返回的結果。我們可以通過創造一個 Promise 鏈來實現這種需求。
var promise = new Promise(function(resolve, reject){resolve('promise') }) promise.then(function(rs){console.log(rs)return 'success' }).then(function(rs){console.log(rs)return new Promise(function(resolve, reject) {setTimeout(function(){reject('error')}, 3000)}) }).then(function(){// 不會執行console.log('不會執行') }, function (rs) {console.log(rs)return 'finish' }).then(function(rs){console.log(rs) })// 輸出 // promise // success // error // finish? ? 注意:一定要有返回值,否則,callback 將無法獲取上一個 Promise 的結果。
6.2、Catch 的后續鏈式操作
? ? 有可能會在一個回調失敗之后繼續使用鏈式操作,即 使用一個 catch,這對于在鏈式操作中拋出一個失敗之后,再次進行新的操作很有用。請閱讀下面的例子:
new Promise((resolve, reject) => {console.log('初始化');resolve(); }) .then(() => {throw new Error('有哪里不對了');console.log('執行「這個」”'); }) .catch(() => {console.log('執行「那個」'); }) .then(() => {console.log('執行「這個」,無論前面發生了什么'); });// 輸出結果 // 初始化 // 執行“那個” // 執行“這個”,無論前面發生了什么? ? 注意:因為拋出了錯誤 有哪里不對了,所以前一個 執行「這個」 沒有被輸出。
6.3、執行順序
? ? 為了避免意外,即使是一個已經變成 resolve 狀態的 Promise,傳遞給 then() 的函數也總是會被異步調用:
Promise.resolve().then(function(){console.log(2) }) console.log(1); // 1, 2? ? 傳遞到 then() 中的函數被置入了一個微任務隊列,而不是立即執行,這意味著它是在 JavaScript 事件隊列的所有運行時結束了,事件隊列被清空之后,才開始執行:
var loading = new Promise(function (resolve, reject) {setTimeout(resolve) }) loading.then(function () {console.log(4) }); Promise.resolve().then(function(){console.log(2) }).then(function(){console.log(3) }); console.log(1); // 1, 2, 3, 4微任務宏任務參考文章[http://www.manongjc.com/article/9144.html。
promise微任務;
setTimeout宏任務。
6.4、嵌套
? ? then回調函數中如果返回了新的異步對象,那么后續鏈式調用的then都會等待新的異步對象完成才會繼續向下執行,如:
var p1 = new Promise(function(resolve, reject){console.log('p1')resolve() }) p1.then(function(){return new Promise(function(resolve, reject) {setTimeout(function(){console.log('p2')resolve()}, 1000)}).then(function(){console.log('p3')}).then(function(){console.log('p4')}) }).then(function(){console.log('p5') }) // 輸出 // p1 // p2 // p3 // p4 // p5? ? 簡便的 Promise 鏈式編程最好保持扁平化,不要嵌套 Promise,因為嵌套會導致可讀性降低,代碼也不容易排除,上述邏輯優化如下:
var p1 = new Promise(function(resolve, reject){console.log('p1')resolve() }) p1.then(function(){return new Promise(function(resolve, reject) {setTimeout(function(){console.log('p2')resolve()}, 1000)}) }).then(function(){console.log('p3') }).then(function(){console.log('p4') }).then(function(){console.log('p5') }) // 輸出 // p1 // p2 // p3 // p4 // p5七、Promise的缺點
代碼有風險造成未解決的promise;
無法取消Promise,一旦新建它就會立即執行,無法中途取消;
如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部;
當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
八、async await
? ? async/await 是 ES7 引入的新的異步代碼 規范,它提供了一種新的編寫異步代碼的方式,這種方式在語法層面提供了一種形式上非常接近于同步代碼的異步非阻塞代碼風格,在此之前我們使用的多是異步回調、 Promise 模式。
使用方式
8.1、async 關鍵字
? ? async function 用來定義一個返回 AsyncFunction 對象的異步函數。異步函數是指通過事件循環異步執行的函數,它會通過一個隱式的 Promise 返回其結果。
async function name([param[, param[, ... param]]]) { statements }? ? name 函數名稱。param 要傳遞給函數的參數。statements 函數體語句。返回值 返回的Promise對象會運行執行(resolve)異步函數的返回結果,或者運行拒絕(reject)——如果異步函數拋出異常的話。
8.2、await 關鍵字
? ? await 操作符用于等待一個Promise 對象。它只能在異步函數 async function 中使用。await 會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled,其回調的resolve函數參數作為 await 表達式的值,繼續執行 async function。若 Promise 處理異常(rejected)await 表達式會把 Promise 的異常原因拋出。另外,如果 await 操作符后的表達式的值不是一個 Promise,則返回該值本身。
[return_value] = await expression;表達式 一個 Promise 對象或者任何要等待的值。
返回值 返回 Promise 對象的處理結果。如果等待的不是 Promise 對象,則返回該值本身。
示例
async function f1() {var x = await resolveAfter2Seconds(10);console.log(x); // 10 } f1();8.3、并聯的 await
? ? async/await 語法確實很簡單好用,但在現實場景中也容易出現一些問題。以下面代碼為例:
async function retriveProfile(email) {const user = await getUser(email);const roles = await getRoles(user);const level = await getLevel(user);return [user, roles, level]; }? ? 上面代碼實現了獲取用戶基本信息,然后通過基本信息獲取用戶角色、級別信息的功能,其中 getRoles 與 getLevel 兩者之間并無依賴,是兩個并聯的異步操作。但代碼中 getLevel 卻需要等待 getRoles resolve 之后才能執行。并不是所有人都會犯這種錯誤,而是同步風格很容易誘惑我們忽略掉真正的異步調用次序,而陷入過于簡化的同步思維中。寫這一段的目的正是為了警醒大家,async 只是形式上的同步,根本上還是異步的,請注意不要讓使用者把時間浪費在無謂的等待上。上面的邏輯,用一種稍微 繞 一些的方式來實現,就可以避免這種性能損耗:
async function retriveProfile(email) {const user = await getUser(email);const p1 = getRoles(user);const p2 = getLevel(user);const [roles, levels] = await Promise.all(p1, p2);return [user, roles, levels]; }? ? 注意,代碼中的 getRoles 、getLevel 函數都沒有跟在 await 關鍵字之后,而是把函數返回的 Promise 存放在變量 p1、p2 中,后續才對 p1、p2 執行 await 聲明, getRoles 、getLevel 就能同時執行,不需等待另一方的完成。
九、錯誤處理
使用try…catch
async function asyncCall() {try {await asyncFunc();throw new Error("oops");} catch (e) {console.log(e);// output// Error: oops at asyncCall (<anonymous>:4:11)} }包裝promise,使其返回統一的格式的代碼
/*** 包裝promise, 使其返回統一的錯誤格式* @param {Promise} promise */ function to (promise) {// 第一個標識異常數據return promise.then(res => [null, res]).catch(err => [err]) } const [err, res] = await to(fetchUser(true)) if (err) {console.error('touser err:', err) }繼續使用catch
// 因為async 返回的promise對象,所以可以使用catchconst user4 = await fetchUser(true).catch(err => {console.error('user4 error:', err)})優點
async/await從上到下,順序執行,就像寫同步代碼一樣。這更符合人編寫代碼的習慣。
缺點
編譯后增加了代碼的體積;
編譯后的代碼不容易理解,會給調試時帶來一定困擾。
十、jQuery 異步對象
? ? jquery中試通過$.Deferred來實例化異步對象的,deferred對象就是jQuery的回調函數解決方案。在英語中,defer的意思是”延遲”,所以deferred對象的含義就是”延遲”到未來。
簡單示例
在 jQuery 中
var?deferred?=?$.Deferred(); var promise = deferred.promise();在 ES6 中
var?promise=?function?(function(resolve,?reject){ })處理狀態
? ? 一個 deferred對象 能做的和一個promise對象差不多,它也有resolve和reject兩個函數來觸發 done()和fail()函數 。
在jQuery中
var deferred = $.Deferred(); setTimeout(function(){deferred.resolve('success') }, 2000) var promise = deferred.promise();在 ES6 中
var promise = new Promise(function(resolve, reject){resolve('success') })? ? jquery的延時對象可以在外面解決,二promise的異步對象在實例化的回調中解決,相比promise的更加安全,語法也簡潔。
等待多個異步完成
在jQuery中
var deferred1 = $.Deferred(); deferred1.resolve('promise1'); var promise1 = deferred1.promise(); var deferred2 = $.Deferred(); deferred2.resolve('promise2'); var promise2 = deferred2.promise(); $.when(promise1, promise2).then(function(rs1, rs2){console.log(rs1, rs2)// promise1, promise2 })在 ES6 中
var promise1 = Promise.resolve('promise1') var promise2 = Promise.resolve('promise2') Promise.all([promise1, promise2]).then(function(rs){console.log(rs) // ['promise1', 'promise2'] })成功或失敗都觸發
在jQuery中
var deferred1 = $.Deferred(); deferred1.reject('promise1'); var promise1 = deferred1.promise(); promise1.always(function(rs){console.log(rs) // promise1 }) var deferred2 = $.Deferred(); deferred2.resolve('promise2'); var promise2 = deferred2.promise(); promise2.always(function(rs){console.log(rs) // promise2 })在 ES6 中
var promise1 = Promise.reject('promise1') var promise2 = Promise.resolve('promise2') promise1.finally(function(rs){console.log(rs) // undefined }) promise2.finally(function(rs){console.log(rs) // undefined })ES6中Promise的finally回調中并不會有解決的結果。
十一、Promise常見應用場景
按鈕鎖定,防止重復提交
? ? 實際場景中往往存在用戶提交后,需要等待后臺邏輯完畢后,才能執行其它的操作,那么這個過程中需要鎖定界面相關操作按鈕的,防止用戶做其他不符合業務的操作,如還沒等表單保存完畢就發起審批,那么這種情況代碼中應該怎么處理,下面從代碼層面簡單說明一下:
// main.js module.exports = {// 是否保存中saving: false,// 保存按鈕點擊onSaveClick: function () {var me = this// 先判斷如果處于保存中,不允許再次提交保存if (this.saving) {return}this.save().then(function(){// 釋放saving狀態,可繼續執行保存操作me.saving = false}, function(){// 失敗時saving也要處理為falseme.saving = false})},// 審批按鈕點擊onApplyClick: function () {// 如果處于保存中,不允許執行審批操作if (this.saving) {return}...},// 保存save: function () {return new Promise(function(resolve, reject){// 向后端發起請求Ajax({... }).then(resolve, reject)})} }? ? 這里主要是借助了save方法返回了異步Promise對象來完成對saving狀態的釋放,實際的場景會比這個更復雜,比如校驗或者按鈕的loading效果,但是這僅是邏輯與交互的疊加,主要的處理點還是這里。
實現串行任務隊列
? ? 有些表單提交前是需要做校驗的,比如新增簽約用戶,需要校驗客戶的身份證號以及手機號的正確性后才能保存,并且保存完畢后需要彈出提示,這就需要借助Promise的請求依賴完成這個需求場景,相關代碼如下:
// form.js module.exports = { // 校驗客戶名稱是否重名validateName: function () {return new Promise(function(resolve, reject) {...resolve()})},// 校驗手機號是否存在validateMobile: function () {// Ajax僅表示通過jQuery或者平臺ajax返回的異步對象return Ajax({...})},// 校驗身份證號正確性validateIdNumber: function () {// 如果要使用串行調用方式,即使是前端校驗也要返回Promise對象,如return new Promise(function(resolve, reject) {// 偽代碼,實際應用時不做參考if (/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(this.formData.idNumber)) {resolve()} else {reject()}})},// 保存表單saveForm: function () {var me = thisreturn me.validateName().then(function(){return me.validateMobile()}).then(function(){return me.validateIdNumber()}).then(function(){return Ajax({...})}).then(function(){alert('保存成功')}, function () {alert('保存失敗')})},// 用戶點擊保存按鈕onSaveClick: function () {return this.saveForm()} }? ? 這個場景中,如果用戶的名稱校驗失敗了,那么不會走到后面的校驗手機號,直接會到最后的保存失敗,這也是符合實際業務的,只有前面的校驗全部通過了才會到最后的保存,整個環節是串行的任務執行。
多個異步任務并發控制
? ? 在頁面初始化的時候,往往會發送很多的請求從后端獲取數據,在這個階段頁面會顯示一個loading遮罩層,等所有的請求完畢后再將這個遮罩層關閉掉,這個時候需要借助到Promise并發多個異步,同時監聽異步完成后的動作,大致的代碼如下:
// main.js module.exports = {// 頁面初始化pageInit: function () {// 頁面開始loadingthis.pageLoading = true// 加載用戶數據、證件類型數據、購房信息數據,Promise.all會監聽異步隊列完畢后執行then中的回調,具體細節可參考上訴Promise.all的用法Promise.all([this.getUserData(),this.getCertificateData(),this.getBuyRoomInfo()]).then(function(){// 頁面關掉loadingthis.pageLoading = false}, function () {// 頁面關掉loadingthis.pageLoading = false})},// 獲取用戶數據getUserData: function () {return new Promise(function(resolve, reject) {...resolve({userName: 'xxx',...})})},// 獲取證件類型數據getCertificateData: function () {// Ajax僅表示通過jQuery或者平臺ajax返回的異步對象return Ajax({...})},// 獲取用戶購買房間信息getBuyRoomInfo: function () {return new Promise(function(resolve, reject) {...resolve({roomName: '房間名稱',...})})} }小結
? ? Promise是一種很好的編程設計思想,在設計一些對外的耗時異步API時候,也可以借助Promise,而非傳統的callback方式,可以很優雅的對外輸出。同時這又需要團隊內的成員都需要熟悉Promise的使用,才能更好的發揮它的威力。
------ END ------
作者簡介
楊同學:?研發工程師,目前負責ERP建模平臺的設計與開發工作。
也許您還想看
從案例角度解析建模平臺動態規則引擎
WEB頁面前端性能診斷方法與實踐
【復雜系統遷移 .NET Core平臺系列】之界面層
鏈路追蹤在ERP系統中的應用實踐
總結
以上是生活随笔為你收集整理的前端异步对象的原理与使用方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core分布式项目实战(
- 下一篇: 2017年html5行业报告,云适配发布