您好,登錄后才能下訂單哦!
相信很多開發者都遇到過回調地獄的問題。由于微信小程序的API基本都是基于回調函數的異步操作,如果不使用其他框架或者封裝API,特別是使用較多的wx.request(),基本很快就會遇到回調地獄的問題,維護起來十分痛苦。
舉個例子
假設此時在正在開發一個社交小程序,其中有一個功能的是,小程序用戶在登錄后,可以查看附近的人。
假設使用以下的實現思路,我們通過wx.getLocation()獲取用戶當前位置,然后通過wx.request()請求后端數據。但在此之前需要登錄,參考之前官方文檔推薦的登錄方式,先調用wx.login()獲取code,再用wx.request()請求開發者服務器,成功返回自定義登錄態(一般為access_token或其他令牌形式),之后再用自定義登錄態請求業務數據。
為了方便看,我把官方文檔里的登錄流程貼出來??
思路確定后,開始嘗試coding(以下代碼不建議看完)
/* 以下為Page對象的方法 */ getNearby: function() { // 判斷是否已認證,可采用wx.checkSession()方案 if (isAuth) { // TODO: 獲取業務數據 return } // wx.login獲取code wx.login({ success(res) { if (res.code) { // 獲取自定義登錄態 wx.request({ url, method, headers, data, success(res) { // 請求成功 if (res.statuCode === 200) { // 讀取響應體中的自定義登錄態 let token = res.data.token // 保存自定義登錄態 wx.setStorageSync("assess_token", token) // 獲取位置信息 wx.getLocation({ success(res) { let { latitude, longitude } = res // 請求業務數據 wx.request({ url, method, header, data: { latitude, longitude }, success(res) { // 請求成功 if (res.statuCode === 200) { let data = res.data // 數據渲染到V層 this.setData({ list: data }) } // 請求失敗 else if (res.statuCode === 400) { // TODO } // 其他錯誤情況狀態碼處理 // TODO }, fail(err) { // 調用失敗處理 } }) }, fail(err) { // 調用失敗處理 } }) } // 請求失敗 else if (res.statuCode == 400) { // TODO } // 其他錯誤情況的狀態碼處理 }, fail(err) { // 調用失敗處理 } }) } else { // TODO // 登錄失敗 } }, fail(err) { // wx.login()調用失敗處理 // TODO: ... } }) }
回調地獄出現了。氣功波代碼,別說別人,就連自己看都會覺得惡心。
某天英明的產品經理站了出來,說我們可以加點XXXXX,你可能還得找個地方嵌套其他微信接口或者多加幾個if else
分支,到時候就找個地方哭吧。
解決方案
從某種意義上來說,當今風暴式的前端生態,仰仗于Node以及ES6+的出現。
ES6后對于異步有多種解決方案。一種是采用generator/yield
,但generator
函數使用起來其實比較麻煩。另外一種是采用Promise
,相對比較簡單。ES7也可以采用async/await,
但本質上async/awai
t也是基于Promise
。下面介紹Promise
。
Promise
Promise創建
創建Promise很簡單,Promise本身是一個構造函數。通過new創建。構造函數的參數為一個回調函數,回調函數有兩個參數為resolve和reject(無需手動維護)。resolve和reject是用來改變狀態。關于狀態放到后邊講。
// Promise實例的創建 let p = new Promise((resolve, reject) => { // TODO })
Promise有個缺點,一旦創建便會立刻執行。所以一般會用一個函數進行包裝。
let getPromise = () => { return new Promise((resolve, reject) => { // TODO }) }
Promise狀態
Promise實例有三種狀態,pending
、resolved
和rejected
,Promise
實例創建后就會處于pending
狀態。回調函數中的resolve
和reject
就是用來改變Promise
實例狀態的。當調用resolve
時,Promise
實例會從pending
變成resolved
狀態,表示成功。當調用reject
時,Promise
實例會從pending
變成rejected
狀態,表示失敗。
let getPromise = () => { return new Promise((resolve, reject) => { // TODO // 處理結果 if (result) { resolve(successObject) } else { reject(error) } }) }
常用方法
最常用的方法為then
()和catch
()這兩個方法,通過then
()的傳遞效用就可以解決回調地獄的問題。
其中then
()可接收兩個參數,都是回調函數,第一個回調函數用來處理resolved
狀態,參數為Promise
實例調用resolve傳遞的成功對象。第二回調函數用來處理rejected
狀態,參數為調用Promise
實例調用reject
傳遞的錯誤對象。
實際中then()
我們一般只用來處理resolved的情況,即只傳遞第一個回調函數。對于rejected
情況更多是采用catch
()統一處理。
let getPromise = () => { return new Promise((resolve, reject) => { // TODO // 處理結果 if (result) { resolve(successObject) } else { reject(error) } }) } getPromise() .then(res => { console.log(res) // TODO }) .catch(err => { //TODO })
使用then()
方法可以繼續返回一個Promise
對象,通過return
一個新的Promise
,可以持續的向下傳遞。
getPromise() .then(res => { //第一層Promise console.log(res) // TODO return getPromise() ) .then(res => { // 第二層Promise console.log(res) // TODO }) .catch(err => { // TODO })
其他常用方法有諸如Promise.all()
,Promise.race()
。當需要等待多個Promise
結果時會采用。兩個方法都是接收一個由Promise
組成的對象數組。使用Promise.all()
時,只有當全部的Promise
對象全部resolved Promise.all()
狀態才是resolved
。而Promise.race()只需有一個Promise
對象為resolved
時,其狀態就為resolved。
更多方法可閱讀相關文檔。
封裝小程序接口
學習了Promise基礎,通過封裝異步操作,使用Promise鏈就可以解決回調地獄問題。
因為wx.request()使用頻率比較高,先對wx.request()封裝。
/* 可以將公用的方法掛在app.js中 */ request: function(method, url, header, data) { return new Promise((resolve, reject) => { wx.request({ method, url, header, data, success(res) { resolve(res) }, fail(err) { reject(err) } }) }) }
基本框架就這樣,我們可以進一步修改,比如請求url的基礎路徑,添加一些公用的header,針對狀態碼做一些全局處理等。
request: function(method, url, header = {}, data = {}) { // 啟動時可將storage中的令牌掛到app.js let token = app.assess_token if (token) { header["Authorization"] = token } return new Promise((resolve, reject) => { wx.request({ method, url: "https://api.domain.com/v1" + url, header, data, success(res) { // 請求成功 if (res.statusCode === 200) { resolve(res) } // 請求成功無響應體 else if (res.statusCode === 204) { /* 可做一些成功提示, 如調用wx.showToast()、wx.showModal()或自定義彈出層等 */ resolve(res) } // 未認證 else if (res.statusCode === 401) { /* 可做一些錯誤提示,或者直接跳轉至登錄頁面等 */ reject(res) } else if (res.statusCode == 400) { /* 可做一些錯誤提示*/ reject(res) } else if (res.statuCode === 403) { /* 無權限錯誤提示*/ reject(res) } // ...其他狀態碼處理 }, fail(err) { /* 可做一些全局錯誤提示,如網絡錯誤等 */ reject(err) } }) }) }
封裝之后,舉個例子,發送請求就可以修改為
/* 方法體中 */ let app = getApp() app.request("POST", "/auth", {}, { username, password }) .then(res => { // 第一層請求 // TODO 成功處理 return app.request("GET", "/goods", {}, {}) }) .then(res => { // 第二層請求 // TODO 成功處理 // 渲染視圖 }) .catch(err => { // TODO 錯誤處理 })
封裝一下其他的微信接口
/* 可以將公用的方法掛在app.js中 */ wxLogin: function() { return new Promise((resovle, reject) => { wx.login({ success(res) { if (res.code) { resovle(res) } else { reject({ message: "登錄失敗" }) } }, fail(err) { reject(err) } }) }) } getLocation: function() { return new Promise((resolve, reject) => { wx.getLocation({ success(res) { resolve(res) }, fail(err) { reject(err) } }) }) }
對于最初的例子,可以就修改為
/* Page對象的方法 */ getNearby: function() { // 判斷是否已認證,可采用wx.checkSession()方案 if (isAuth) { // TODO: 獲取業務數據 return } app.wxLogin() .then(res => { // 將code發送給開發者服務器,獲取自定義登錄態 return app.request("POST", "/auth", {}, { code, res.code }) }) .then(res => { // 保存自定義登錄態 setStorage("access_token", res.data.access_token) // TODO: 其他登錄成功操作... return app.getLocation() }) .then(({ latitude, longitude }) => { let url = "/nearby?latitude=" + latitude + "&longitude=" + longitude return app.request("GET", url) }) .then(res => { // TODO: 數據處理 let data = res.data // 渲染視圖層 this.setData({ data }) }) .catch(err => { // TODO 錯誤處理 }) }
之后若有需添加新的請求或者其他異步操作,直接在Promise鏈上操作就行了。
以上就是Promise實踐 實現微信小程序接口封裝的詳細內容,更多請關注億速云其它相關文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。