微信小程序中使用Promise進行異步流程處理

咱們知道,JavaScript是單進程執行的,同步操做會對程序的執行進行阻塞處理。好比在瀏覽器頁面程序中,若是一段同步的代碼須要執行很長時間(好比一個很大的循環操做),則頁面會產生卡死的現象。php

因此,在JavaScript中,提供了一些異步特性,爲程序提供了性能和體驗上的益處,好比能夠將代碼放到setTimeout()中執行;或者在網頁中,咱們使用Ajax的方式向服務器端作異步數據請求。這些異步的代碼不會阻塞當前的界面主進程,界面仍是能夠靈活的進行操做,等到異步代碼執行完成,再作相應的處理。git

一段典型的異步代碼相似這樣:小程序

function asyncFunc(callback) {
  setTimeout(function () {
    //在這裏寫你的邏輯代碼
    //...

    //邏輯代碼結束,執行一個回調函數
    callback();
  }, 5000);
}

或者:微信小程序

function getAccountInfo(callback, errorCallback) {
  wx.request({
    url: '/accounts/12345',
    success: function (res) {
      //...
      callback(data);
    },
    fail: function (res) {
      //...
      errorCallback(data);
    }
  });
}

而後咱們這樣調用:瀏覽器

asyncFunc(function () {
  console.log("asyncFunc() run complete");
});

getAccountInfo(function (data) {
  console.log("get account info successfully:", data);
}, function () {
  console.error("get account info failed");
});

這是一種使用了回調函數來控制代碼執行流程的方式。這樣看起來沒問題,也挺容易理解。服務器

可是,若是咱們一段代碼中,異步操做太多,又要保證這些異步操做是有順序的執行,那咱們的代碼就看起來很是糟糕,就像這樣:微信

asyncFunc1(function(){
  //...
  asyncFunc2(function(){
    //...
    asyncFunc3(function(){
      //...
      asyncFunc4(function(){
        //...
        asyncFunc5(function(){
           //...
        });
      });
    });
  });
});

這樣的代碼可讀性和可維護性可想而知了。還有,回調函數真正的問題在於:app

它剝奪了咱們使用 return 和 throw 這些關鍵字的能力。
那有什麼辦法來改善這個問題呢?答案是確定的,Promise這種概念的產生,很好地解決了這一切。關於什麼是Promise,一搜一大把介紹,我這裏就不復制粘貼了,我主要是講一下咱們怎麼用它來解決咱們的問題。框架

咱們來看一下,上面的例子若是使用Promise,它會是什麼樣子?咱們先將這些函數變成Promise的方式:異步

function asyncFunc1(){
  return new Promise(function (resolve, reject) {
    //...
  })
}

// asyncFunc2,3,4,5也實現成跟asyncFunc1同樣的方式...
而後看一下他們是怎麼樣被調用的:

asyncFunc1()
  .then(asyncFunc2)
  .then(asyncFunc3)
  .then(asyncFunc4)
  .then(asyncFunc5);

這樣,這些異步函數就會按照順序一個一個依次執行了。

ES6中原生支持了Promise,不過在原生不支持Promise的環境中,咱們有不少第三方庫來支持,好比Q.js和Bluebird。它們通常都除了提供標準Promise的API外,還提供了一些標準以外但很是有用的API,使得異步流程的控制更加優雅。

從微信小程序的API文檔中咱們能夠看到,框架提供的JavaScript API中不少函數其實都是異步的,如wx.setStorage(), wx.getStorage(), wx.getLocation()等等,它們也是提供的回調的處理方式,在參數中傳入success, fail,complete回調函數,就能夠對運行成功或失敗進行分別處理。

如:

wx.getLocation({ 
  type: 'wgs84', 
  success: function(res) { 
    var latitude = res.latitude 
    var longitude = res.longitude 
    var speed = res.speed 
    var accuracy = res.accuracy 
  },
  fail: function() {
    console.error("get location failed")
  }
})

咱們能不能讓微信小程序的異步API支持Promise呢?答案是確定的,咱們固然能夠一個一個的用Promise去包裝這些API,可是這個仍是比較麻煩的。不過,因爲小程序的API的參數格式都比較統一,只接受一個object參數,回調都是在這個參數中設置,因此,這爲咱們的統一處理提供了便利,咱們能夠寫一個非侵入性的工具方法,來完成這樣的工做:

假設咱們將這個工具方法寫到一個名爲的util.js的文件中:

var Promise = require('../libs/bluebird.min')  //我用了bluebird.js

function wxPromisify(fn) {  
  return function (obj = {}) {    
    return new Promise((resolve, reject) => {      
      obj.success = function (res) {        
        resolve(res)      
      }      

      obj.fail = function (res) {        
        reject(res)      
      }      

      fn(obj)    
    })  
  }
}

module.exports = {  
  wxPromisify: wxPromisify
}

以後,咱們來看一下如何使用這個方法,將原來回調方式的API變成Promise的方式:

var util = require('../utils/util')

var getLocationPromisified = util.wxPromisify(wx.getLocation)

getLocationPromisified({
  type: 'wgs84'
}).then(function (res) {
  var latitude = res.latitude 
  var longitude = res.longitude 
  var speed = res.speed 
  var accuracy = res.accuracy 
}).catch(function () {
  console.error("get location failed")
})

是否是很容易理解?

關於使用Promise處理異步流程,就先講到這裏,有什麼疑問,能夠留言給我。不對之處,歡迎指正。
相關閱讀:
一斤代碼深刻理解系列《一》:微信小程序事件機制
一斤代碼深刻理解系列《二》:微信小程序樣式機制
一斤代碼深刻理解系列《三》:微信小程序和服務器通訊

相關文章
相關標籤/搜索