本文详解微信小程序中Promise.all与顺序执行的区别。针对小程序单次并发请求限制10个的问题,介绍如何通过封装sequenceTasks函数,将Promise.all的并发执行改为顺序执行,从而支持批量上传超过10张图片而不报错。
微信小程序Promise.all与顺序执行详解
前言与需求背景
一、前言
近期在进行小程序开发时,遇到一个表单提交需求:表单中包含图片字段。微信平台的处理机制要求先上传图片,后台返回图片名称和地址后,再将图片信息插入表单并提交。这里的核心问题是如何确保图片上传完成后,再执行表单提交。由于小程序中多图上传需逐张进行,因此需要解决多个图片请求完成后,再发起表单提交的业务逻辑。
Promise.all与Promise.race的区别
二、Promise.all和Promise.race
首先介绍Promise.all与Promise.race的区别。Promise.all(iterable)方法接收一个可迭代对象(通常为数组),当其中所有Promise对象都完成(resolve)或第一个失败(reject)时,返回一个新的Promise对象。
需要注意,Promise.all是并发执行多个Promise对象,返回的Promise对象的参数是一个数组,数组中的各项也是可迭代对象执行的顺序返回。
Promise.race(iterable)方法返回一个新的Promise,只要iterable中有一个Promise对象完成(resolve)或失败(reject),新的Promise就会立即完成(resolve)或失败(reject),并获得之前那个Promise对象的返回值或者错误原因。所以只要iterable中有一个完成或者失败就立即返回一个promise对象。根据race这个单词为赛跑也能得出,最先到达的立即返回一个promise对象。
基于上述定义,我们采用Promise.all方法来完成我们的需求。
//存储promise对象的数组
let promiseArr = [];
//图片地址数组
let imageList = [];
//将图片地址的上传的promise对象加入到promiseArr
for (let i = 0; i < imageList.length; i++) {
let promise = new Promise((resolve, reject) => {
//微信图片上传
wx.uploadFile({
url: 'https://xxx.xxx.xxx/api/uploadImage',
filePath: imageList[i],
name: 'file',
success: function(res) {
//可以对res进行处理,然后resolve返回
resolve(res);
},
fail: function (error) {
reject(error);
},
complete: function (res) {
},
})
});
promiseArr.push(promise)
}
//Promise.all处理promiseArr数组中的每一个promise对象
Promise.all(promiseArr).then((result) => {
//对返回的result数组进行处理
})
微信小程序并发限制问题
三、微信小程序的问题
在实现微信小程序图片上传功能时,需先上传图片,并将图片名和地址通过response返回。
虽然使用了Promise.all方法,但存在一个问题:Promise.all是并发执行的,而微信小程序一次只能并发10个请求。
对于图片上传场景,可能需要一次上传超过10张图片,即一次并发超过10个请求,这会导致微信报错。
报错信息为:“WAService.js:4 uploadFile:fail createUploadTask:fail exceed max upload connection count 10”。

顺序执行Promise解决方案
四、顺序Promise执行处理
由于Promise.all同时运行多个Promise对象,而小程序对并发数量有限制(一次只能并发10个),因此若想突破此限制,可以对每个Promise进行顺序执行。
具体实现代码如下:
//顺序处理函数
function sequenceTasks(tasks) {
//记录返回值
function recordValue(results, value) {
results.push(value);
return results;
}
let pushValue = recordValue.bind(null, []);
let promise = Promise.resolve();
// 处理tasks数组中的每个函数对象
for (let i = 0; i < tasks.length; i++) {
let task = tasks[i];
promise = promise.then(task).then(pushValue);
}
return promise;
}
//函数数组,每个函数的返回值是一个promise对象
let promiseFuncArr = [];
//图片地址数组
let imageList = [];
//将图片地址的上传的函数加入到promiseFuncArr数组中
for (let i = 0; i < imageList.length; i++) {
let promiseTemp = function(){
return new Promise((resolve, reject) => {
//微信图片上传
wx.uploadFile({
url: 'https://xxx.xxx.xxx/api/uploadImage',
filePath: imageList[i],
name: 'file',
success: function(res) {
//可以对res进行处理,然后resolve返回
resolve(res);
},
fail: function (error) {
reject(error);
},
complete: function (res) {
},
})
});
};
promiseFuncArr.push(promiseTemp)
}
sequenceTasks(promiseFuncArr).then((result) => {
//对返回的result数组进行处理
});
1. 这里解释一下sequenceTasks函数的作用
首先,recordValue函数接收两个参数:results(返回数组)和value(传入值)。执行results.push(value),将每个值推入results数组,然后返回results数组。
let pushValue = recordValue.bind(null, []);
pushValue是一个函数对象,将recordValue绑定到一个空数组[]中。第一个参数传null代表不改变函数this的指向,因此pushValue得到的是一个function (value)的函数,参数传入value。
promise = promise.then(task).then(pushValue);
task是函数,返回Promise对象,在此处即为上传图片函数。每张图片上传都创建一个函数,通过.then(pushValue),pushValue是function (value)的函数,value代表图片上传后的返回值。pushValue将返回值推入result数组,依次执行并加入result数组,最后返回。这样即可得到一个对象数组,数组中即为依次执行返回的结果。
2. sequenceTasks里面的for循环,也可以写成如下的reduce方式:
function sequenceTasks(tasks) {
//记录返回值
function recordValue(results, value) {
results.push(value);
return results;
}
let pushValue = recordValue.bind(null, []);
return tasks.reduce(function (promise, task) {
return promise.then(task).then(pushValue);
}, Promise.resolve());
}
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!