赞
踩
问题描述:动态获取图片宽、高。由于图片大小不一,导致异步请求返回时间有差异,频繁操作导致渲染结果出现问题。
举例说明:【图片2】尺寸较大,返回需要【2s】;【图片1】尺寸最小,返回需要【1s】。先点击【图片2】然后迅速点击【图片1】,最后渲染和获取的结果为【图片2】==> 最后操作的是【图片1】,按预期应该展示【图片1】。
该问题,在两个以上异步请求控制同一展示内容时都会出现: 第一个异步请求(耗时长)发出没有返回,又操作触发了第二个请求(耗时短)。第二个请求先返回,内容呈现;此时第一个异步请求才返回,导致了内容呈现成第一个请求的结果。和操作预期不符(期望呈现的是后点击的–第二个请求的结果)。
上述示例代码实现:
// 显示图片尺寸
async function showImageSize(id) {
const imageBlob = await fetch(new Request(`http://localhost:8888/images?name=${id}`)).then(r => r.blob())
const img = await createImageBitmap(imageBlob)
render(img)
}
document.querySelector('#btn').onclick = () => showImageSize(id)
function render(img) {
// document.querySelector('img').src = URL.createObjectURL(imageBlob)
document.querySelector('#result').innerHTML = `${img.width}px * ${img.height}px`
}
这里,对于 fetch
增加中止请求很简单
const controller = new AbortController()
const { signal } = controller
const imageBlob = await fetch(
new Request(`http://localhost:8888/images?name=${id}`),
{ signal }
).then(r => r.blob())
// 要求取消 fetch 请求,在相应的时机调取即可
controller.abort()
当 abort()
被调用时,fetch()
promise 拒绝一个名为 AbortError
的 DOMException
但问题是,createImageBitmap()
不支持中止操作。 我们通过辅助函数形式进行处理:
async function abortable(signal, promise) {
if (signal.aborted) throw new DOMException('AbortError', 'AbortError')
return Promise.race([
promise,
new Promise((resolve, reject) => {
signal.addEventListener('abort', () => {
reject(new DOMException('AbortError', 'AbortError'));
})
})
])
}
修改 showImageSize()
函数
let controller async function showImageSize(id) { if (controller) controller.abort() try { controller = new AbortController() const { signal } = controller const imageBlob = await fetch( new Request(`http://localhost:8888/images?name=${id}`), { signal } ).then(r => r.blob()) const img = await abortable(signal, createImageBitmap(imageBlob)) } catch (error) { if (error.name === 'AbortError') return throw error; } }
后端服务(模拟)
const url = require('url') const path = require('path') const fs = require('fs') server.on('request', (req, res) => { req.on('end', () => { const querys = url.parse(req.url).query // 可查看相关「Form 表单数据编码、解码」文章 const searchParams = new URLSearchParams(querys) const fileName = searchParams.get('name') const filePath = path.resolve(__dirname, `../images/${fileName}.png`) const cs = fs.createReadStream(filePath) cs.on("data", chunk => { res.write(chunk) }) cs.on("end", () => { res.end() }) }) })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。