JavaScript中等待Parse Library项保存完成解决优先级重复问题
问题现象
对数据项做优先级排序时,编写的循环处理逻辑输出结果不符合预期:
- 预期优先级序列:
[1,2,3,4,5] - 实际输出序列:
[1,1,2,2,3] - 测试验证:在循环中添加
alert增加执行延迟后,可以得到正确输出
对应实现的switchPriority异步函数代码如下:
const switchPriority = async function(catId, srcI, destI){ let indexofCat; try { for (let i = 0; i < data[0].length; i++) { const element = data[0][i]; if(element.id === catId){ indexofCat = i; } } let Item = Parse.Object.extend('Item') let itemQuery = new Parse.Query(Item) for (let i = (srcI>destI?destI:srcI); i < (srcI>destI?(srcI+1):(destI+1)); i++) { let id = data[1][indexofCat][i].id; let item = await itemQuery.get(id); item.set("priority",i+1); await item.save(); } } catch (error) { alert(error) } }
问题根因
两个问题叠加导致优先级重复:
- 本地状态与后端数据不同步:优先级计算完全依赖内存中缓存的
data数组,但每次item.save()更新后端数据后,没有同步修改本地data里对应项的优先级和排序位置。如果短时间内多次触发排序(比如拖拽操作的连续事件触发),后续执行的逻辑会读取到旧的data快照,重复计算出相同的优先级值写入后端。 - 异步操作没有并发控制:
switchPriority是异步函数,如果上一次函数内的所有save操作还没执行完就触发了下一次调用,两个异步流程会交叉执行,互相覆盖写入的优先级值。
加alert后能得到正确结果,本质是弹窗阻塞了JS主线程,既等来了上一批异步保存的完成,也给了本地状态同步更新的时间,不会出现旧数据读入、流程交叉的问题,所以结果正常。
修复方案
- 每次执行
save操作后,立刻同步更新本地data数组中对应项的优先级和排序位置,不要等后端查询返回再更新本地状态,保证所有后续逻辑读取到的都是最新值。 - 给
switchPriority加执行锁,函数执行开始时置锁为占用状态,所有循环内的保存操作全部完成后再释放锁,锁占用期间直接忽略新的排序触发请求,避免多个异步流程并发交叉执行。 - 优化请求逻辑:循环内不需要每次都调用
itemQuery.get(id)拉取最新对象,已知操作项id的前提下,可以直接创建Parse对象实例执行更新,减少网络请求耗时,降低并发冲突的概率。
修改后的核心参考代码:
// 排序操作执行锁 let isSwitching = false; const switchPriority = async function(catId, srcI, destI){ // 锁占用时直接返回,避免并发 if(isSwitching) return; isSwitching = true; let indexofCat; try { for (let i = 0; i < data[0].length; i++) { const element = data[0][i]; if(element.id === catId){ indexofCat = i; // 找到目标分类后直接终止循环,减少无效遍历 break; } } let Item = Parse.Object.extend('Item') const start = srcI>destI?destI:srcI; const end = srcI>destI?(srcI+1):(destI+1); for (let i = start; i < end; i++) { const currentItem = data[1][indexofCat][i]; const id = currentItem.id; // 跳过多余get请求,直接创建对象实例更新 let item = new Item(); item.id = id; item.set("priority",i+1); await item.save(); // 同步更新本地缓存状态 currentItem.priority = i+1; } } catch (error) { alert(error) } finally { // 无论操作成功失败,最终都释放锁 isSwitching = false; } }
内容的提问来源于stack exchange,提问作者aborted
相关产品推荐
相关产品推荐

