鄒捷
(國(guó)防大學(xué)政治學(xué)院,西安 710068)
分析微信小程序異步API 的特點(diǎn),針對(duì)其在某些應(yīng)用場(chǎng)景下不適用的情況,給出一種高效、通用的解決方案,實(shí)現(xiàn)在調(diào)用異步方式獲取的關(guān)鍵變量之前,檢查、等待、確保變量有意義的同步操作。
微信小程序;異步;同步化
微信小程序自2017年1月推出以來,保持了高速發(fā)展。據(jù)統(tǒng)計(jì),2018年底小程序數(shù)量已經(jīng)超過120 萬(wàn),用戶數(shù)量突破7 億,用戶對(duì)小程序的使用習(xí)慣已經(jīng)形成。小程序成功的一個(gè)很大的原因,就是相對(duì)于App而言,其開發(fā)難度大大降低。開發(fā)者只需具備一定的Web 及前端開發(fā)的經(jīng)驗(yàn),就可以通過小程序開發(fā)框架,簡(jiǎn)單、高效地開發(fā)出媲美原生App 的應(yīng)用。小程序框架系統(tǒng)分為視圖層和邏輯層兩部分,視圖層主要通過視圖層描述語(yǔ)言WXML 和WXSS 完成開發(fā),其功能與HTML 和CSS 類似;邏輯層通過JavaScript 進(jìn)行開發(fā)。在開發(fā)實(shí)踐中,由于小程序API 的設(shè)計(jì)和限制,邏輯層的處理和常規(guī)腳本有一定的區(qū)別。本文針對(duì)微信小程序API 異步處理方式帶來的一些問題進(jìn)行分析,并結(jié)合實(shí)踐提出一套可行的解決方案。
微信小程序提供了大量的API,如用戶界面、網(wǎng)絡(luò)訪問、數(shù)據(jù)緩存、媒體訪問、開放接口等,大多數(shù)API 都采用了異步方式處理返回值。如發(fā)起網(wǎng)絡(luò)請(qǐng)求的wx.request[1],其處理方式如下:
wx.request({
wx.request 提交網(wǎng)絡(luò)訪問請(qǐng)求,訪問Web 頁(yè)面https://test.com/test.php,該頁(yè)面既可以返回標(biāo)準(zhǔn)的HTML 代碼,也可以返回JSON 字符串形式的數(shù)據(jù),對(duì)應(yīng)的處理在success 指定的回調(diào)函數(shù)中進(jìn)行。微信小程序中,對(duì)于回調(diào)的處理都采用了異步方式,也就意味著request 訪問請(qǐng)求提交后,程序并不會(huì)停下來等待返回值,而是繼續(xù)執(zhí)行request 后續(xù)的代碼。當(dāng)頁(yè)面的返回值到達(dá)時(shí),再執(zhí)行success 指定的函數(shù)。這種異步方式處理的好處是程序執(zhí)行效率高,由于網(wǎng)絡(luò)速度各有不同,返回的時(shí)間可能很快,也可能很慢,采用異步方式處理返回值,不會(huì)受網(wǎng)速影響出現(xiàn)卡頓現(xiàn)象。
但在一些特定的應(yīng)用場(chǎng)景下,異步方式也存在不足。
第一,代碼結(jié)構(gòu)變得復(fù)雜。由于返回值處理代碼必須寫在success 處理函數(shù)中,如果有多個(gè)相互依賴的網(wǎng)絡(luò)訪問,結(jié)構(gòu)就必須多次嵌套,可讀性將大大降低。如獲取用戶信息getUserInfo 必須要在獲取配置信息getSetting 成功之后執(zhí)行,其結(jié)構(gòu)就變?yōu)椋?/p>
第二,對(duì)于一些需要從網(wǎng)絡(luò)獲取的關(guān)鍵變量,在小程序的多個(gè)頁(yè)面或多個(gè)位置,都可能被調(diào)用。但因?yàn)閺木W(wǎng)絡(luò)獲取方法wx.request 是異步方式的,因此不能保證在需要調(diào)用該關(guān)鍵變量時(shí),它已經(jīng)被獲取到了。例如用戶唯一標(biāo)識(shí)openid 需要通過網(wǎng)絡(luò)獲取,盡管小程序一運(yùn)行就優(yōu)先執(zhí)行了獲取操作getOpenid,但由于是異步操作,可能openid 還沒有從網(wǎng)絡(luò)傳輸過來,程序已經(jīng)執(zhí)行到某個(gè)需要用到openid 的代碼了,這種情況將導(dǎo)致程序異常。
問題二比問題一更加突出,因?yàn)橛玫給penid 的地方很多,即使想把處理代碼都搬到getOpenid 的success回調(diào)函數(shù)中也是不可能的。解決方法只能是對(duì)小程序中通過異步方式獲取的數(shù)據(jù),增加同步點(diǎn)。在網(wǎng)絡(luò)返回結(jié)果到達(dá)之前,不往后執(zhí)行,等待直到網(wǎng)絡(luò)返回結(jié)果到達(dá)或者超時(shí)。
查詢網(wǎng)絡(luò)上的類似解決方案,一種方法是使用Promise 對(duì)象實(shí)現(xiàn)異步轉(zhuǎn)同步[2],但在小程序的JS 環(huán)境中無法通過。另一種方法是在調(diào)用關(guān)鍵變量之前,判斷變量是否存在。如果變量尚未賦值,則等待一個(gè)時(shí)間片后再判斷。但由于JavaScript 是單線程處理,這種方法最終將導(dǎo)致CPU 時(shí)間被占滿,程序卡死。
經(jīng)過反復(fù)實(shí)踐,筆者找到一種可行的關(guān)鍵變量同步方法,并將其通用化。各種需要等待同步的關(guān)鍵變量,都可以使用這種方法。其核心是waitVar 函數(shù),實(shí)現(xiàn)對(duì)變量的等待。傳遞給它的有三個(gè)參數(shù):key 是變量的名稱,可以保證多個(gè)調(diào)用可以同時(shí)執(zhí)行;varb 是關(guān)鍵變量本身;fun 是調(diào)用關(guān)鍵變量的函數(shù),由waitVar 在必要的時(shí)候調(diào)用該函數(shù)。當(dāng)varb 已經(jīng)有值的時(shí)候,wait-Var 返回1;當(dāng)varb 尚未賦值的時(shí)候,返回-1,并且會(huì)在500 毫秒后再次運(yùn)行fun 函數(shù);waitVar 保持一個(gè)內(nèi)部計(jì)數(shù),當(dāng)嘗試次數(shù)過多時(shí)認(rèn)定為超時(shí),返回-2,不再運(yùn)行fun 函數(shù)。waitVar 核心代碼如下:
//等待指定變量,返回:1:成功-1:等待-2:超時(shí)
在需要同步變量的函數(shù)的開始位置調(diào)用waitVar,如果返回-1 或-2,則直接退出,由waitVar 控制在500毫秒后重新進(jìn)入該函數(shù);如果返回1,則正常往下執(zhí)行。如上述的getCataTag 函數(shù)中,必須要用到openid,可以在函數(shù)開始處調(diào)用waitVar。
getCataTag:function(overwrite){
if (util.waitVar("openid", this.openid, this.getCataTag) <0)return //強(qiáng)制等待openid
……//后續(xù)代碼
當(dāng)waitVar 返回1,意味著openid 已經(jīng)有值了,繼續(xù)向下執(zhí)行;如果返回值為-1,則退出,由waitVar 在500 毫秒后重新調(diào)用getCataTag 函數(shù);如果返回-2,顯示超時(shí)提示并退出后,不會(huì)再進(jìn)入getCataTag 函數(shù)。這樣的處理,既保證了程序運(yùn)行順暢,也考慮到極端網(wǎng)絡(luò)超時(shí)的情況,確保程序不會(huì)崩潰。
圖1 網(wǎng)絡(luò)異常時(shí)提示
除了openid,其他任何類似情況都可以使用這種機(jī)制。如graph.js 是圖表顯示模塊,當(dāng)從其他頁(yè)面跳轉(zhuǎn)到圖表頁(yè)面時(shí),會(huì)先通過網(wǎng)絡(luò)獲取圖表所需的數(shù)據(jù)并存儲(chǔ)在allinfo 數(shù)組中,然后在graph 的onShow 函數(shù)中顯示圖表。因此,必須要保證onShow 執(zhí)行時(shí)allinfo 中的數(shù)據(jù)已經(jīng)獲取到,使用waitVar 就能夠確保這一同步操作。
onShow: function () { //顯示圖表,前提是數(shù)組allinfo 必須已經(jīng)有值
//如果數(shù)組allinfo 尚無數(shù)據(jù),則退出并在500 毫秒后重新執(zhí)行onShow
//如果數(shù)組allinfo 已經(jīng)有數(shù)據(jù),則繼續(xù)執(zhí)行后續(xù)圖表顯示代碼
if (util.waitVar("graphallinfo", allinfo, this.onShow) <0)return
……//后續(xù)代碼
以下是實(shí)際運(yùn)行效果,依次同步等待了兩個(gè)關(guān)鍵變量:openid 和info_catatag,且info_catatag 的依賴于openid。圖2 可以看出,程序在兩個(gè)同步點(diǎn)同時(shí)等待,直到openid 和info_catatag 依次取得數(shù)據(jù),程序再繼續(xù)向下運(yùn)行。
圖2 依次同步openid和info_catatag
本文針對(duì)微信小程序異步API 在某些應(yīng)用場(chǎng)景下不適用的情況,給出了一種高效、通用的同步方法。該方法經(jīng)過多個(gè)小程序的應(yīng)用檢驗(yàn),在各種網(wǎng)絡(luò)條件和各種機(jī)型的手機(jī)上,都能夠順暢地運(yùn)作,對(duì)于小程序開發(fā)有一定的參考價(jià)值。