关于小程序网络数据请求延迟导致页面渲染失败问题

问题

微信小程序 wx.request(ajax)网络请求默认为异步,当 wx.request 发出请求后,程序流程将继续向下执行,此时 wxml 页面若有服务器返回的数据绑定,将输出一个空白。当异步请求完毕数据返回后,wxml 页面已经渲染完毕,返回的数据并不会显示在页面。

还有一种情况,在以 tabBar 结构的小程序里,当第一个页面正在执行 wx.request 数据请求,在外部数据返回页面前,用户快速切换到了其它 tabBar 页面,此时容易造成第一个页面数据返回中断。

解决方法1: 定义回调函数

回调函数的运行逻辑是这样的,有 A → B 两个按顺序运行的过程,A 开始执行异步数据请求,B 需要 A 返回的数据渲染页面,可以在 B 中定义一个 A.callback 回调函数。当过程 A 成功返回数据后判断 this.callback 是否被定义,如果回调被定义,就说明 B 正需要这个数据,别犹豫了赶紧执行吧;如果数据返回后 A 没发现 this.callback 被定义,说明数据返回的太快了,B 还没开始产生需要呢,于是 A 把返回的数据存入手机缓存或全局变量里,等 B 需要的时候自己取。而 B 发现全局变量后,就无需再定义 A.callback 函数,当然即使定义了 A 也不会执行,因为 A 已经运行结束。

示例代码

用小程序开发工具默认的代码,优化后运行如下:
在这里插入图片描述

app.js 全局脚本文件

//app.js
App({
  onLaunch: function () {
    // 获取用户头像和昵称信息,需要用户授权
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            success: res => {
              // 此处更新全局变量,但 index.onLoad 可能已经执行完毕
              this.gbData.userInfo = res.userInfo;
              // 由于 getUserInfo 是网络请求,可能会在 index.onLoad 完成之后返回
              // 所以此处判断回调函数是否被 index.onLoad 定义,如果定义则执行之
              if (this.userInfoCallback) {
                this.userInfoCallback(res);
              }
            }
          })
        }
      }
    })
  },
  gbData: {
    userInfo: null // 存储用户的头像路径和昵称信息的全局变量
  }
})

index.js 页面脚本文件

//index.js
const app = getApp();
Page({
  data: {
    motto: '欢迎登录',
    userInfo: {},
    hasUserInfo: false //如果用户未授权,则不能获取用户信息
  },
  onLoad: function () {
    // 由于 app.js 中的 getUserInfo 是异步网络请求, 全局变量 userInfo
    // 可能没有被返回的数据填充,所以必须在 else 中给 app 定义一个回调函数
    if (app.gbData.userInfo) {
      this.setData({
        userInfo: app.gbData.userInfo,
        hasUserInfo: true
      });
    } else {
      // 给 app 定义回调函数并在 app 中执行,接收 res 参数进行页面数据渲染
      app.userInfoCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        });
      }
    }
  },
  // 用户点击授权时响应函数,进行全局变量更新及页面数据渲染
  f0(e) {
    app.gbData.userInfo = e.detail.userInfo;
    this.setData({
      userInfo: app.gbData.userInfo,
      hasUserInfo: true
    });
  }
})

index.wxml 页面渲染结构

<!--index.wxml-->
<view class="container">
	<view class="userinfo">
		<button wx:if="{{!hasUserInfo}}" open-type="getUserInfo" bindgetuserinfo="f0">获取头像昵称</button>
		<block wx:else>
			<image class="avatar" src="{{userInfo.avatarUrl}}"></image>
			<text>{{userInfo.nickName}}</text>
		</block>
	</view>
	<view>{{motto}}</view>
</view>

index.css 页面样式文件

/**index.wxss**/
.container, .userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.container {
  height: 100vh;
  justify-content: space-around;
}

.avatar {
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
}

index.json 文件保留空对象 {} 即可。
还有一种情况,如果同时存在 C 过程也需要返回数据,并且在 C 定义了一个 A.callback,此时在 A 中执行 this.callback 将会产生一个线程错误。因此这种回调方法可以用在程序结构简单的地方。

解决方法2:使用 promise 对象

promise 是 ES6 的新增功能,小程序内核支持,功能强大,详见阮一峰大神的教程: https://es6.ruanyifeng.com/#docs/promise

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yxp_xa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值