小程序异步问题之多个网络请求依次执行并依次收集请求结果

业务逻辑

最近开发一个便签小程序的时候,有这样一个需求:用户可以在写便签的时候添加一个或多个图片。

对于这个需求,我们用户按下保存键时,内部具体的实现上是这样的逻辑:

  1. 首先检测用户是否传入了图片,如果存储本地图片地址的数组长度>=1,则将图片数组放入上传图片的函数。
  2. 由于小程序网络请求大小限制,我们只能采取循环上传单文件,然后收集每次请求的结果--图片在服务器的地址,最后将结果放在一个数组中供后续的操作使用。
  3. 当图片上传函数全部执行完毕后,将数组中的图片数组取出来,赋值到日记对象中,再将整个日记对象提交到服务器。
  4. 服务器返回保存成功或失败。

思路其实非常清晰简单,但是在代码实现上却翻了大跟头。

异步带来的问题

小程序的网络请求是异步的:我们无法通过return来将网络请求结果返回出来使用。

  wx.request({

     //...省略其他属性

     success: function (res) {

     },

     fail: function (res) {

     }

  })

例如在微信中发送网络请求,我们只能使用微信提供的方法wx.xxx,其中请求的结果保存在res中,而res无法直接return得到。

解决:res虽然无法直接获取,但是我们能通过将需要使用到这个请求结果的业务逻辑代码放入这个网络请求的回调函数中直接读取网络请求结果,也就是一切都需要通过回调来解决。

  wx.request({

     //...省略其他属性

     success: function (res) {

      console.log(res);

      //接业务逻辑代码

     },

     fail: function (res) {

      console.log(res);

     }

  })

例如这个微信的网络请求,我们可以通过success和fail的回调函数来读取res的值从而完成依赖res结果的业务逻辑。

回调地狱

虽然解决了结果获取的问题,但是又产生了另一个问题,当多个请求中有明确的先后顺序时,回调会嵌套的很厉害,造成回调地狱,代码可读性和可维护性都会很差。

例如对于一个日记页面,需要先请求到页面的数据(里面包含了图片数据和其他数据的地址),再根据页面数据去请求图片数据后再请求音频数据。例如以下代码:

  //请求页面整体数据

  wx.request({

     //...省略其他属性

     success: function (res) {//成功

        //请求图片数据

        wx.request({

         success: function (res) {//成功

           //请求音频数据

           wx.request({

             success: function (res) {//成功

             },

             fail: function (res) {//失败

               console.log("请求失败:"+res);

             }

           })

         },

         fail: function (res) {//失败

           console.log("请求失败:"+res);

         }

        })

     },

     fail: function (res) {//失败

       console.log("请求失败:"+res);

     }

  })

如何优化?幸运的是,在es6里面我们可以用promise去优化我们的回调,用then代替回调,首先将网络请求封装成一个Promise:

// 后台post请求

  function postRequest(posturl, postdata) {

   return new Promise((resolve, reject) => {

    wx.request({

     //省略其他属性

     success: function (res) {

      console.log("at post request: 请求成功")

      resolve(res.data)//设置promise成功标志

     },

     fail: function (res) {

      console.log("at post request: 请求失败")

      reject(res.data)//设置promise失败标志

     }

    })

   });

  }

这样封装以后,我们的网络请求会在success和fail后回调resolve,这样可以告诉promise,“hey,我完成我的工作了,你可以进行你的then操作了”,这样就可以用then来简化嵌套逻辑。使用promise来完成上面那个问题的请求将会是这样的:

 postRequest(posturl,postdata)

  .then(function(res){

   //业务逻辑

   //调用下一个请求

   return postRequest(next_posturl,next_postdata);

  })

  .then(function(res){

   //业务逻辑

   //调用下一个请求

   return postRequest(next_next_posturl,next_next_postdata);

  })

  .then(function(res){

   //业务逻辑

  });

是不是简洁的多~

一个看似简单的需求

我们的有一个很简单的需求是需要对一组数量不定的图片做分别上传(因为微信限制所以无法做多上传),并且在上传完成以后需要获取到所有的返回结果。

那么用我们前面的回调函数+then的话,很自然的想到这样的写法

postRequest(posturl,postdata)

.then(function(res){

  //获取返回res

  //上传下一个图片

  return postRequest(next_posturl,next_postdata);

})

.then(function(res){

  //获取返回res

  //上传下一个图片

  return postRequest(next_next_posturl,next_next_postdata);

})

.then(function(res){

  //获取返回res

});

这样看起来很简单明了,但是我的图片数量是不定的,怎么动态的构建.then.then.then这样的链式调用呢?经过我的研究后发现可以通过一个辅助的promise链去完成主链的链式构建。

//多文件上传

function jabingp_upLoad(uploadurl, files) {

 return new Promise((resolve, reject) => {

  //初始化promise链

  var mergedAjax = Promise.resolve();

  var response = [];

  // 循环上传

  // 这里一定要使用let来为没一次循环构建一个块级作用域
  // 使用var则需要配合立即执行函数
  for (let i = 0; i < files.length; i++) {

   mergedAjax = mergedAjax.then(() => {

    return jabingp_upLoadSingle(uploadurl, files[i]).then((res) => {

     response.push(res);

    });

   });

  }

  //当前面循环中所有的then执行完毕时会执行这个then

  mergedAjax.then(() => {

   resolve(response);      //设置这个函数的promise对象为完成状态并放入数据

  });

 });

}

通过这个函数,就完成了多个请求依次执行并收集结果的效果。这个函数的重点在于利用另外一个已经处于完成状态的promise,不断的迭代自身,在每次迭代的then内部通过return来完成辅助链到业务链的切换。

2019-04-27 更新

使用await/async更加优雅地处理异步吧!

在es7标准中,引入了await和async这对兄弟,它们可以让我们的异步代码看起来和同步代码一样。让我们来看看await和async都能做什么吧。

await可以等待一个promise运行到完成状态并且获取结果,或者等待一个async修饰的函数运行完成并获取结果,但是使用await的时候,必须在async函数体内部。比如我有这样一个网络请求:

 function postRequest(posturl, postdata) {

   return new Promise((resolve, reject) => {

    wx.request({

     //省略其他属性

     success: function (res) {

      console.log("at post request: 请求成功")

      resolve(res.data)//设置promise成功标志

     },

     fail: function (res) {

      console.log("at post request: 请求失败")

      reject(res.data)//设置promise失败标志

     }

    })

   });

  }

那么如果不使用await,我就需要这样取得请求结果

function test(){
 postRequest(xxx,xxx).then(function(res){
   // 这里面可以读取请求结果res了
   console.log(res);
 });
}
test();

可以看到,这样的代码不太符合常规逻辑,我们希望函数作用是返回数据,这样更清晰明了,有了await,我们的愿望就可以实现了。

async function test(){
 let res = await postRequest(xxx,xxx);
 // 下面就可以正常写对res的读取了
 console.log(res);
}
test();

注意我给函数加上了async,有了async和await,我们就可以像同步代码一样使用异步请求了~

那么上面那个通过复杂的构建链完成的需求,通过await实现将会变得非常简单易懂。

async function jabingp_upLoad(uploadurl, files) {
  let response = [];

  // 循环依次等待上传结果
  for (let i = 0; i < files.length; i++) {
    let res = await jabingp_upLoadSingle(uploadurl, files[i]);
    // 结果放入数组
    response.push(res);
  }
  // 返回结果
  return response ;
}

代码一下子变得简洁易懂了,注意调用的时候也同样需要在一个async函数内部执行await。

async function test(){
 let response = await jabingp_upLoad(xxx,xxx);
 console.log(response );
}
test();

是不是非常简单呢,赶紧在你的异步请求中使用async和await吧~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 微信小程序中使用Promise进行异步流程处理的实例详解

    微信小程序中使用Promise进行异步流程处理的实例详解 我们知道,JavaScript是单进程执行的,同步操作会对程序的执行进行阻塞处理.比如在浏览器页面程序中,如果一段同步的代码需要执行很长时间(比如一个很大的循环操作),则页面会产生卡死的现象. 所以,在JavaScript中,提供了一些异步特性,为程序提供了性能和体验上的益处,比如可以将代码放到setTimeout()中执行:或者在网页中,我们使用Ajax的方式向服务器端做异步数据请求.这些异步的代码不会阻塞当前的界面主进程,界面还是可以

  • 微信小程序 缓存(本地缓存、异步缓存、同步缓存)详解

    微信小程序 缓存 关于本地缓存 1.wx.setStorage(wx.setStorageSync).wx.getStorage(wx.getStorageSync).wx.clearStorage(wx.clearStorageSync) 可以对本地缓存进行设置.获取和清理.本地缓存最大为10MB 2.localStorage 是永久存储 一.异步缓存 wx.setStorage(OBJECT) 将数据存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容 wx.setStor

  • 小程序开发中如何使用async-await并封装公共异步请求的方法

    前言 在平常的项目开发中肯定会遇到同步异步执行的问题,还有的就是当执行某一个操作依赖上一个执行所返回的结果,那么这个时候你会如何解决这个问题呢: 1.是用settimeout让它异步执行,显然这只是让它加入异步任务队列中去执行,但并不能保证等待其返回结果再去执行另一个操作. 2.还是自己封装callback函数?那样就会陷入所谓的回调地狱,代码层层嵌套,环环相扣,逻辑稍微复杂就会很难去维护. 3.当然es6中的promise倒是很好的解决了这样的问题,再配合es7的async和await就更完美

  • 微信小程序 wx.request方法的异步封装实例详解

    wx-promise-request 是对微信小程序 wx.request 方法的异步封装. 解决问题 支持 Promise (使用 es6-promise 库). 管理请求队列,解决 request 最大并发数超过 10 会报错的问题. 下载 npm install wx-promise-request 然后拷贝 dist/index.js 文件到你的小程序项目中. 使用 import {request} from './wx-promise-request'; request({ url:

  • 详解微信小程序 同步异步解决办法

    详解微信小程序 同步异步解决办法 小程序中函数体还没有完成,下一个函数就开始执行了,而且两个函数之间需要传参.那是因为微信小程序函数是异步执行的.但微信小程序增加了ES6的promise特性支持,微信小程序新版本中移除了promise的支持,需要自己使用第三方库来自行实现ES6的promise特性. WxService.js import Tools from 'Tools' import es6 from '../assets/plugins/es6-promise' class Servic

  • 微信小程序异步API为Promise简化异步编程的操作方法

    把微信小程序异步API转化为Promise.用Promise处理异步操作有多方便,谁用谁知道. 微信官方没有给出Promise API来处理异步操作,而官方API异步的又非常多,这使得多异步编程会层层回调,代码一复杂,回调起来就想砸电脑. 于是写了一个通用工具,把微信官方的异步API转化为Promise,方便处理(多)异步操作. 你可以这样用: 准备转化后的方法并暴露出 // /utils/wx-promise.js import toPromise from '/module/to-promi

  • 微信小程序中使用Async-await方法异步请求变为同步请求方法

    微信小程序中有些 Api 是异步的,无法直接进行同步处理.例如:wx.request.wx.showToast.wx.showLoading等.如果需要同步处理,可以使用如下方法: 注意: Async-await方法属于ES7语法,在小程序开发工具中如果勾选es6转es5, 会报错: ReferenceError: regeneratorRuntime is not defined 避免报错,可以引入 regenerator 在根目录下创建 lib 文件夹,并将 https://github.c

  • 微信小程序异步处理详解

    本文实例为大家分享了微信小程序异步处理的具体方法,供大家参考,具体内容如下 直接看问题: 然后看打印的结果: 根据上面两图可以看出,代码上先执行的网络请求,再执行打印的变量,但是从下面打印的结果来看,先出结果的是执行打印变量的函数(aafn函数),再打印出网络请求success的回调里返回的数据和赋值后的变量的值: 为什么先执行的aafn,并且打印的值没有赋值上? 因为wx.request是一个异步的请求,所以数据请求的同时,可以继续向下执行函数.所以这里值还没有赋值上就开始打印了变量的值: 这

  • 小程序异步问题之多个网络请求依次执行并依次收集请求结果

    业务逻辑 最近开发一个便签小程序的时候,有这样一个需求:用户可以在写便签的时候添加一个或多个图片. 对于这个需求,我们用户按下保存键时,内部具体的实现上是这样的逻辑: 首先检测用户是否传入了图片,如果存储本地图片地址的数组长度>=1,则将图片数组放入上传图片的函数. 由于小程序网络请求大小限制,我们只能采取循环上传单文件,然后收集每次请求的结果--图片在服务器的地址,最后将结果放在一个数组中供后续的操作使用. 当图片上传函数全部执行完毕后,将数组中的图片数组取出来,赋值到日记对象中,再将整个日记

  • 微信小程序中如何使用flyio封装网络请求

    Flyio简介 Fly.js 通过在不同 JavaScript 运行时通过在底层切换不同的 Http Engine来实现多环境支持,但同时对用户层提供统一.标准的Promise API.不仅如此,Fly.js还支持请求/响应拦截器.自动转化JSON.请求转发等功能,详情请参考:https://github.com/wendux/fly . 下面我们看看在微信小程序.mpvue中和中如何使用fly. Flyio 官方地址 文档 github地址 Flyio的一些特点 fly.js 是一个基于 pr

  • 在小程序/mpvue中使用flyio发起网络请求的方法

    Fly.js 一个基于Promise的.强大的.支持多种JavaScript运行时的http请求库. 有了它,您可以使用一份http请求代码在浏览器.微信小程序.Weex.Node.React Native.快应用中都能正常运行.同时可以方便配合主流前端框架 ,最大可能的实现 Write Once Run Everywhere. Flyio Github: https://github.com/wendux/fly 问题 随着 Weex .mpvue 的发布,他们都是支持Vue.js语法.目前v

  • 微信小程序中网络请求缓存的解决方法

    需求 提交小程序审核时,有一个体验测评,产品让我们根据小程序的体验测评报告去优化小程序. 其中有一项是网络请求的优化,给我们出了很大的难题. 文档中是这样解释的:3分钟以内同一个url请求不出现两次回包大于128KB且一模一样的内容 看到这个问题的时候,首先想到的是在响应头上加上cache-control,经过测试发现小程序并不支持网路请求缓存.搜索发现官方明确答复,小程序不支持网络请求缓存:wx.request不支持http缓存 既然官方不支持网络请求缓存,那只能自己想办法解决这个问题了. 先

  • 微信小程序网络请求模块封装的具体实现

    目录 前言 具体实现 1.确定结构 2.service网络请求的基本配置和公共方法 3.apis创建对应功能模块的网络请求方法 4.使用 总结 前言 在进行一个微信小程序项目开发的时候我需要网络请求模块和后端数据进行沟通: 接下来就和我一起简单的封装的一下网络请求模块吧. 具体实现 1. 确定结构 在根目录新建 service和apis文件夹: service用来做网络请求的基本配置和输出公共的网络请求方法: apis用来创建对应功能模块的网络请求方法(ex:操作音乐.操作视频): 2. ser

  • 微信小程序设置http请求的步骤详解

    http请求介绍 HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则.计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务器)请求信息和服务,HTTP目前协议的版本是1.1.HTTP是一种无状态的协议,无状态是指Web浏览器和Web服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP遵循请

  • 微信小程序 数据交互与渲染实例详解

    微信小程序 数据交互与渲染 实现效果图: 微信小程序的api中提供了网络交互的api,我们只要调用即可和后端进行数据交互,该api为wx.request.,具体代码如下. //list.js //获取应用实例 var app = getApp() Page({ data: { list:[], hiddenLoading: true, url: '' }, loadList: function () { var that = this; that.setData({ hiddenLoading:

  • 微信小程序 http请求详细介绍

    在微信小程序进行网络通信,只能和指定的域名进行通信,微信小程序包括四种类型的网络请求. 普通HTTPS请求(wx.request) 上传文件(wx.uploadFile) 下载文件(wx.downloadFile) WebSocket通信(wx.connectSocket) 这里以介绍wx.request,wx.uploadFile,wx.dowloadFile三种网络请求为主 设置域名 要微信小程序进行网络通信,必须先设置域名,不然会出现错误: URL 域名不合法,请在 mp 后台配置后重试

随机推荐