相信很多開發者都曾經遇到過回檔地獄的問題。由於微信小程式的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實務 實作微信小程式介面封裝的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。