JavaScript 异步时序问题异步时序问题
场景场景
死后我们必升天堂,因为活时我们已在地狱。
不知你是否遇到过,向后台发送了多次异步请求,结果最后显示的数据却并不正确 – 是旧的数据。
具体情况:
用户触发事件,发送了第 1 次请求
用户触发事件,发送了第 2 次请求
第 2 次请求成功,更新页面上的数据
第 1 次请求成功,更新页面上的数据
嗯?是不是感觉到异常了?这便是多次异步请求时会遇到的异步回调顺序与调用顺序不同的问题。
思考思考
为什么会出现这种问题?
出现这种问题怎么解决?
为什么会出现这种问题?为什么会出现这种问题?
JavaScript 随处可见异步,但实际上并不是那么好控制。用户与 UI 交互,触发事件及其对应的处理函数,函数执行异步操作
(网络请求),异步操作得到结果的时间(顺序)是不确定的,所以响应到 UI 上的时间就不确定,如果触发事件的频率较高/
异步操作的时间过长,就会造成前面的异步操作结果覆盖后面的异步操作结果。
关键点
异步操作得到结果的时间(顺序)是不确定的
如果触发事件的频率较高/异步操作的时间过长
出现这种问题怎么解决?出现这种问题怎么解决?
既然关键点由两个要素组成,那么,只要破坏了任意一个即可。
手动控制异步返回结果的顺序
降低触发频率并限制异步超时时间
手动控制返回结果的顺序手动控制返回结果的顺序
根据对异步操作结果处理情况的不同也有三种不同的思路
后面异步操作得到结果后等待前面的异步操作返回结果
后面异步操作得到结果后放弃前面的异步操作返回结果
依次处理每一个异步操作,等待上一个异步操作完成之后再执行下一个
这里先引入一个公共的 wait 函数
/**
* 等待指定的时间/等待指定表达式成立
* 如果未指定等待条件则立刻执行
* 注: 此实现在 nodejs 10- 会存在宏任务与微任务的问题,切记 async-await 本质上还是 Promise 的语法糖,实际上并非真正的同步函数!!!即便在浏览
器,也不要依赖于这种特性。
* @param param 等待时间/等待条件
* @returns Promise 对象
*/
function wait(param) {
return new Promise(resolve => {
if (typeof param === 'number') {
setTimeout(resolve, param)
} else if (typeof param === 'function') {
const timer = setInterval(() => {
if (param()) {
clearInterval(timer)
resolve()
}
}, 100)
} else {
resolve()
}
})