目录

遇到的问题

最近在开发小程序,API实现了权限控制,需要先获取到token然后使用token请求接口,但是就遇到了一个问题,由于小程序wx.request()方法是异步的,在app.js执行request后,各方法加载app.js的全局数据时,无法按顺序加载,所以就出现了在token还未获取到就去请求其它API,导致API调用失败。

微信小程序之请求异步带来的问题

问题代码

const api = require('config/api.js');
App({
  globalData: {
    version: "1.5",
    token: '',
    uid: ''
  },
  onLaunch: function () {
    this.login();
    this.getAppInfo();
    this.getTabList();
  },
  login: function () {
    wx.login({
      success: function (res) {
        wx.request({
          url: api.wxAppLogin,
          header: {
            'x-auth-wechat-app-code': res.code
          },
          success: function (res) {
            if (res.data.code != 1) {
              this.registerUser();
              return;
            }
            this.globalData.token = res.data.data.token;
            this.globalData.uid = res.data.data.id;
          }
        })
      }
    })
  },
  getAppInfo() {
    wx.request({
      url: api.option,
      header: {
        'x-auth-token': this.globalData.token
      }
      data: {
        name: 'title'
      },
      success: function (res) {
        if (res.data.code == 1) {
          wx.setStorageSync('mallName', res.data[0].value);
        }
      }
    })
  },
  getTabList(){
    ...
  }
})

例如上面的代码,其实代码本没有错,但在这种场景下就必须得到解决。

login()还未执行完毕,getAppInfo()getTabList()可能就已经执行了,所以getAppInfo()getTabList()拿token时肯定就拿不到,那么API肯定也就执行错误了。

解决问题

如果我们一段代码中,异步操作太多,又要保证这些异步操作是有顺序的执行,那我们的代码就看起来非常糟糕,就像这样的极端情况:

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

多层回调嵌套,业务复杂了就有些不忍直视了,找了一下资料,看到大家推荐引入ES6的promise,如果我们改用Promise来处理,那么进行一层简单的包装即可。

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

然后我看到了这个解决方案 微信小程序之请求异步带来的问题

https://github.com/cinoliu/-es6-promise

引入promiss后,然后看了一下他实现的util:wxRequest.js,但这里好像还并不能满足我的需求,这里实现的方法不能自定义header,所以自己改了一下,以get方法为例:

function getRequest(url, data, header) {
  var getRequest = wxPromisify(wx.request)
  return getRequest({
    url: url,
    method: 'GET',
    data: data,
    header: header
  })
}

使用后代码大概是这样:

//index.js
const api = require('config/api.js');
var util = require('../../utils/util')
var wxApi = require('../../utils/wxApi')
var wxRequest = require('../../utils/wxRequest')
var app = getApp()
Page({
  data: {
    version: "1.5",
    token: '',
    uid: ''
  },
  onLoad: function () {
    var that = this;
    //1.获取code
    var wxLogin = wxApi.wxLogin()
    wxLogin().then(res => {
      console.log('1.获取code成功了')
      var url = api.wxAppLogin,;
      var params = {},
      var header = {
        'x-auth-wechat-app-code': res.code
      }
      //2.获取token
      return wxRequest.getRequest(url, params, header)
    }).
      then(res => {
        console.log('2.获取token成功了')
        this.globalData.token = res.data.data.token;
        this.globalData.uid = res.data.data.id;
        var url = api.option,
        var params = {
          name: 'title'
        }
        var header = {
          'x-auth-token': res.data.data.token
        }
        return wxRequest.postRequest(url, params, header)
      }).
      then(res => {
        console.log('3.获取app信息成功了')
        console.log(res)
        //4.获取系统信息
        var wxGetSystemInfo = wxApi.wxGetSystemInfo()
        return wxGetSystemInfo()
      }).
      then(res => {
        console.log('4.成功了')
        console.log(res)
        //5.获取用户信息
        var wxGetUserInfo = wxApi.wxGetUserInfo()
        return wxGetUserInfo()
      })
      .finally(function (res) {
        console.log('finally~')
        wx.hideToast()
      })
  }
})

这样就能实现一步步走了,就不会存在文章开头说的情况了,但是我发现了一个问题,我觉得es-promise这个文件稍微有点大,就想着能不能有其他的办法,然后又看到了这个 微信小程序之请求异步带来的问题

https://github.com/skyvow/wx-extend

也是开源的小程序拓展插件,看了他实现的WxService - Promise API插件,看了一下,感觉更干净,也能解决我的问题,所以就用了一下。

import wxService from './plugins/WxService'
const api = require('config/api.js');
App({
  globalData: {
    version: "1.5",
    token: '',
    uid: '',
  },
  onLaunch: function () {
    this.getStorageToken();
  },
  //获取token并赋值到全局变量token
  getStorageToken() {
    this.wxService = new wxService;
    console.log(this.wxService)
    //从缓存获取token
    this.wxService.getStorage({
      key: 'token'
    }).then(
      res => {
        console.log("get token:", res);
        this.globalData.token = res.data;
        this.getSiteInfo();
        this.getTabList();
      },
    ).catch(
      err => {
        console.log("从缓存获取token失败")
        this.login();
      }
    )
  },
  login: function(){
    ...
  }
})

嗯至此我的问题也解决了,感谢开源作者,希望文章能帮到您。

参考资料