多次并发调用POST方法出现数据覆盖及异步函数执行顺序疑问
1. 运行中的异步函数是否会被中断?
JS是单线程事件循环模型,同步代码执行时绝对不会被打断,只有当前调用栈所有同步代码全部执行完毕、调用栈为空时,才会依次读取微任务队列、宏任务队列的待执行任务运行。你代码里的printSqrt是纯同步循环,只要它开始执行,所有点击事件回调、Promise回调都会被暂存到任务队列等待,直到循环完全跑完才会被调度执行。
2. 同一个异步函数同时调用的执行顺序
async函数的执行遵循固定规则:
- 调用async函数时,函数内第一个await之前的所有代码都是同步执行,会严格按照调用顺序在调用栈中依次运行,不会被其他代码打断
- 遇到await关键字后,当前函数会暂停执行,await之后的代码会被封装为微任务,等待await的Promise状态变更后,再进入微任务队列排队执行
所以不存在运行中的异步函数被另一个同函数实例中断的情况,所有同步逻辑的执行顺序是完全可控的。
你担心的任务积压场景是真实存在的,当前代码的漏洞在于isExecuting赋值时机不对:
队列清空后,代码先执行同步的printSqrt('In queue'),此时isExecuting仍然为true,如果这个阶段用户点击按钮新增任务到队列,点击事件的判断逻辑会直接返回、不会触发executeQueue。等printSqrt执行完再把isExecuting设为false时,队列里的新任务就失去了执行触发时机,只能等下次点击才会被处理。
修复方案非常简单,调整executeQueue的执行顺序即可:
async function executeQueue() { while (taskQueue.length != 0) { const item = taskQueue.shift() await addToCart(item) } // 先修改执行状态,再运行其他同步逻辑 isExecuting = false printSqrt('In queue') }
调整后就算printSqrt运行期间有新任务进入队列,点击事件判断isExecuting已经为false,就会主动触发executeQueue运行,不会出现任务积压。
补充说明:你最开始遇到的加购数量小于实际点击数的问题,本质是后端并发写冲突:多个加购请求同时到达后端,同时读取当前购物车数量、各自加1后回写,就会出现数据覆盖,你用队列串行发送请求的方案刚好规避了这个并发问题,是合理的解决方案。
内容的提问来源于stack exchange,提问作者noob_coder

