摘要:異步帶來的問題小程序的網絡請求是異步的我們無法通過來將網絡請求結果返回出來使用。省略其他屬性接業務邏輯代碼例如這個微信的網絡請求,我們可以通過和的回調函數來讀取的值從而完成依賴結果的業務邏輯。
業務邏輯
最近開發一個便簽小程序的時候,有這樣一個需求:用戶可以在寫便簽的時候添加一個或多個圖片。
對于這個需求,我們用戶按下保存鍵時,內部具體的實現上是這樣的邏輯:
首先檢測用戶是否傳入了圖片,如果存儲本地圖片地址的數組長度>=1,則將圖片數組放入上傳圖片的函數。
由于小程序網絡請求大小限制,我們只能采取循環上傳單文件,然后收集每次請求的結果--圖片在服務器的地址,最后將結果放在一個數組中供后續的操作使用。
當圖片上傳函數全部執行完畢后,將數組中的圖片數組取出來,賦值到日記對象中,再將整個日記對象提交到服務器。
服務器返回保存成功或失敗。
思路其實非常清晰簡單,但是在代碼實現上卻翻了大跟頭。
異步帶來的問題小程序的網絡請求是異步的:我們無法通過return來將網絡請求結果返回出來使用。
wx.request({ //...省略其他屬性 success: function (res) { }, fail: function (res) { } })
例如在微信中發送網絡請求,我們只能使用微信提供的方法wx.xxx,其中請求的結果保存在res中,而res無法直接return得到。
解決:res雖然無法直接獲取,但是我們能通過將需要使用到這個請求結果的業務邏輯代碼放入這個網絡請求的回調函數中直接讀取網絡請求結果,也就是一切都需要通過回調來解決。
wx.request({ //...省略其他屬性 success: function (res) { console.log(res); //接業務邏輯代碼 }, fail: function (res) { console.log(res); } })
例如這個微信的網絡請求,我們可以通過success和fail的回調函數來讀取res的值從而完成依賴res結果的業務邏輯。
回調地獄雖然解決了結果獲取的問題,但是又產生了另一個問題,當多個請求中有明確的先后順序時,回調會嵌套的很厲害,造成回調地獄,代碼可讀性和可維護性都會很差。
例如對于一個日記頁面,需要先請求到頁面的數據(里面包含了圖片數據和其他數據的地址),再根據頁面數據去請求圖片數據后再請求音頻數據。例如以下代碼:
//請求頁面整體數據 wx.request({ //...省略其他屬性 success: function (res) {//成功 //請求圖片數據 wx.request({ success: function (res) {//成功 //請求音頻數據 wx.request({ success: function (res) {//成功 }, fail: function (res) {//失敗 console.log("請求失敗:"+res); } }) }, fail: function (res) {//失敗 console.log("請求失敗:"+res); } }) }, fail: function (res) {//失敗 console.log("請求失敗:"+res); } })
如何優化?幸運的是,在es6里面我們可以用promise去優化我們的回調,用then代替回調,首先將網絡請求封裝成一個Promise:
// 后臺post請求 function postRequest(posturl, postdata) { return new Promise((resolve, reject) => { wx.request({ //省略其他屬性 success: function (res) { console.log("at post request: 請求成功") resolve(res.data)//設置promise成功標志 }, fail: function (res) { console.log("at post request: 請求失敗") reject(res.data)//設置promise失敗標志 } }) }); }
這樣封裝以后,我們的網絡請求會在success和fail后回調resolve,這樣可以告訴promise,“hey,我完成我的工作了,你可以進行你的then操作了”,這樣就可以用then來簡化嵌套邏輯。使用promise來完成上面那個問題的請求將會是這樣的:
postRequest(posturl,postdata) .then(function(res){ //業務邏輯 //調用下一個請求 return postRequest(next_posturl,next_postdata); }) .then(function(res){ //業務邏輯 //調用下一個請求 return postRequest(next_next_posturl,next_next_postdata); }) .then(function(res){ //業務邏輯 });
是不是簡潔的多~
一個看似簡單的需求我們的有一個很簡單的需求是需要對一組數量不定的圖片做分別上傳(因為微信限制所以無法做多上傳),并且在上傳完成以后需要獲取到所有的返回結果。
那么用我們前面的回調函數+then的話,很自然的想到這樣的寫法
postRequest(posturl,postdata) .then(function(res){ //獲取返回res //上傳下一個圖片 return postRequest(next_posturl,next_postdata); }) .then(function(res){ //獲取返回res //上傳下一個圖片 return postRequest(next_next_posturl,next_next_postdata); }) .then(function(res){ //獲取返回res });
這樣看起來很簡單明了,但是我的圖片數量是不定的,怎么動態的構建.then.then.then這樣的鏈式調用呢?經過我的研究后發現可以通過一個輔助的promise鏈去完成主鏈的鏈式構建。
//多文件上傳 function jabingp_upLoad(uploadurl, files) { return new Promise((resolve, reject) => { //初始化promise鏈 var mergedAjax = Promise.resolve(); var response = []; // 循環上傳 // 這里一定要使用let來為沒一次循環構建一個塊級作用域 // 使用var則需要配合立即執行函數 for (let i = 0; i < files.length; i++) { mergedAjax = mergedAjax.then(() => { return jabingp_upLoadSingle(uploadurl, files[i]).then((res) => { response.push(res); }); }); } //當前面循環中所有的then執行完畢時會執行這個then mergedAjax.then(() => { resolve(response); //設置這個函數的promise對象為完成狀態并放入數據 }); }); }
通過這個函數,就完成了多個請求依次執行并收集結果的效果。這個函數的重點在于利用另外一個已經處于完成狀態的promise,不斷的迭代自身,在每次迭代的then內部通過return來完成輔助鏈到業務鏈的切換。
2019-04-27 更新使用await/async更加優雅地處理異步吧!
在es7標準中,引入了await和async這對兄弟,它們可以讓我們的異步代碼看起來和同步代碼一樣。讓我們來看看await和async都能做什么吧。
await可以等待一個promise運行到完成狀態并且獲取結果,或者等待一個async修飾的函數運行完成并獲取結果,但是使用await的時候,必須在async函數體內部。比如我有這樣一個網絡請求:
function postRequest(posturl, postdata) { return new Promise((resolve, reject) => { wx.request({ //省略其他屬性 success: function (res) { console.log("at post request: 請求成功") resolve(res.data)//設置promise成功標志 }, fail: function (res) { console.log("at post request: 請求失敗") reject(res.data)//設置promise失敗標志 } }) }); }
那么如果不使用await,我就需要這樣取得請求結果
function test(){ postRequest(xxx,xxx).then(function(res){ // 這里面可以讀取請求結果res了 console.log(res); }); } test();
可以看到,這樣的代碼不太符合常規邏輯,我們希望函數作用是返回數據,這樣更清晰明了,有了await,我們的愿望就可以實現了。
async function test(){ let res = await postRequest(xxx,xxx); // 下面就可以正常寫對res的讀取了 console.log(res); } test();
注意我給函數加上了async,有了async和await,我們就可以像同步代碼一樣使用異步請求了~
那么上面那個通過復雜的構建鏈完成的需求,通過await實現將會變得非常簡單易懂。
async function jabingp_upLoad(uploadurl, files) { let response = []; // 循環依次等待上傳結果 for (let i = 0; i < files.length; i++) { let res = await jabingp_upLoadSingle(uploadurl, files[i]); // 結果放入數組 response.push(res); } // 返回結果 return response ; }
代碼一下子變得簡潔易懂了,注意調用的時候也同樣需要在一個async函數內部執行await。
async function test(){ let response = await jabingp_upLoad(xxx,xxx); console.log(response ); } test();
是不是非常簡單呢,趕緊在你的異步請求中使用async和await吧~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104095.html
摘要:打開一個網頁,看到服務器返回給客戶端瀏覽器的各種文件類型圖片構建瀏覽器會遵守一套步驟將文件轉換為樹。因為瀏覽器有渲染線程與引擎線程,為了防止渲染出現不可預期的結果,這兩個線程是互斥的關系。 1. 瀏覽器架構 用戶界面 主進程 內核 渲染引擎 JS 引擎 執行棧 事件觸發線程 消息隊列 微任務 宏任務 網絡異步線程 定時器線程 2. 從輸入 url 到頁面展示...
摘要:是什么在介紹的概念之前,我們需要簡單回顧一下前面源碼閱讀系列文章六中講過的和的概念以及它們和語句的關系。的任務就是實現請求,執行所有涉及到的請求,并依次返回結果。構造出了所有之后,下一步就是執行這些了。 上篇文章 中,我們介紹了數據讀寫過程中 tikv-client 需要解決的幾個具體問題,本文將繼續介紹 tikv-client 里的兩個主要的模塊——負責處理分布式計算的 copIte...
閱讀 1306·2021-10-08 10:04
閱讀 1938·2021-09-04 16:40
閱讀 2547·2019-08-30 13:21
閱讀 2291·2019-08-29 15:10
閱讀 2859·2019-08-29 12:35
閱讀 1200·2019-08-26 17:41
閱讀 3071·2019-08-26 17:03
閱讀 1152·2019-08-26 12:01