在做一个微信小程序的多图片上传功能,采用canvas绘制功能对大图片进行绘制压缩保存。
因为是多选图片上传,所以使用了一个循环对绘制保存。
uploadfilelist.forEach(function(item){
that.canvasimg(item)
})
.....
canvasimg(image){
....
ctx.drawImage(img, 0, 0, picWidth, picHeight); //先画出图片
//延迟600ms,避免部分机型未绘制出图片内容就执行保存操作,导致最终的图片是块白色画板。
setTimeout(() => {
wx.canvasToTempFilePath({
fileType: "jpg",
canvas: canvas,
....
这里有个问题就保存图片时设置了一个600ms后执行保存动作(原因:避免部分机型未绘制出图片内容就执行保存操作,导致最终的图片是块白色画板。),但是用户上传来的图片列表uploadfilelist循环是立即执行的,导致第一张图片还没有保存就绘制了下一张图片,直接覆盖了上一张图片。
得到结果,全部是同一张图片!
延迟使用
setTimeout
,是一个异步操作。这个问题本质上是要解决“按顺序执行异步操作”的问题。先说解决这个问题最简单的办法是用 Promise + async/await。然后下面再来分析,具体一点解决方案在本回答靠后的地方。
一般异步操作是通过回调来连接的,即异步结束触发回调,回调中开始另一个异步操作。回调要按顺序执行本身有点困难,因为组装回调确实 …… 不知道该怎么形容。不过可以采用一个变通的办法,就是“任务队列”。
任务队列是把要做的任务封装成函数放在一个队列中,每完成一个任务,就出列一个继续进行,直到没有任务或者超过一定时限。
先用 setTimeout 模拟一个画图程,需要消耗一定时间,但保证是在 600 毫秒内。
然后使用队列的方法
然而,其实,这些事情 JS 引擎都可以帮我们做,使用 await 即可。但是在使用 await 之前需要先封装 Promise。由于
drawImage()
是模拟的绘图,所以我们不修改这个函数,只对它进行一个 600 毫秒的封装顺便一提,刚查了一下微信小程序的 Canvas API,案例中 drawImage() 之后会调用 draw()
,而 draw() 应该是实际进行绘制,有一个回调函数参数,在绘制完成时触发,这样的话就不需要固定等待 600 毫秒了。
封装 Promise 大概是这样
没有去搭环境测试,所以这个是猜测的,自己验证一下。
文档里也没写得很清楚,但是我记得小程序改进过 API,异步操作在没有 callback 的时候都会返回 Promise,所以都有可能不需要自己封装 Promise。