循环插入数据无法保证顺序?如何以索引为标题异步插入数据库?
你遇到的其实是异步编程里的典型场景:用循环触发异步任务时,所有任务会并发执行,它们的完成顺序完全取决于网络响应和数据库处理的速度,所以会出现“顺序混乱”的情况。先帮你拆解问题,再给出针对性的解决方案:
问题根源分析
你用lodash的times循环调用createJob时,所有50个异步请求会被一次性触发,每个请求的await axios.post是独立等待响应的——谁先拿到服务器的返回,谁就先完成插入并打印日志。不过这里要澄清一点:你传入的索引i其实是正确传递给title的(因为times的回调每次都会把当前迭代的i作为参数传给createJob,不存在闭包陷阱),数据库里的title字段应该都是正确的0-49(或1-50),只是完成顺序和插入的时间戳会乱。
如果你的需求是必须保证插入到数据库的顺序严格遵循1→50(比如created_at严格递增,或者记录的插入顺序和索引一致),那就要用串行执行的方式;如果只是要确保title正确,同时想提高执行效率,就用并发加结果追踪的方式。
方案1:串行执行(严格保证插入顺序)
把times替换成普通的for循环,配合await让每个任务等待前一个完成后再执行:
const axios = require('axios'); module.exports = (async () => { try { const createJob = async (i) => { console.log(`开始创建任务 ${i}`); const { data } = await axios.post('http://localhost:3000/job/create', { title: i, created_at: Date.now() }); if (data) { console.log(`任务 ${i} 创建完成:`, data); } }; // 逐个执行,等待前一个任务完成再启动下一个 for (let i = 1; i <= 50; i++) { // 如果你需要从1开始就这么写 await createJob(i); } console.log('所有任务已按顺序完成'); } catch (error) { console.error('任务执行出错:', error); } })();
这种方式下,每个任务都会在前一个完全完成后才开始,不仅title正确,created_at也会严格递增,数据库的插入顺序完全和索引一致。
方案2:并发执行(高效且保证title正确)
如果不需要严格的插入顺序,只是想确保每个title对应的索引正确,同时提高执行效率,可以用Promise.all来管理所有异步任务:
const { times } = require('lodash'); const axios = require('axios'); module.exports = (async () => { try { const createJob = async (i) => { console.log(`开始处理任务 ${i}`); const { data } = await axios.post('http://localhost:3000/job/create', { title: i, created_at: Date.now() }); return { index: i, result: data }; // 把索引和结果一起返回 }; // 创建所有任务的Promise数组,并发执行 const jobPromises = times(50, (i) => createJob(i + 1)); // 从1开始的话加1 // 等待所有任务完成 const allResults = await Promise.all(jobPromises); // 按索引排序结果,方便查看顺序 allResults.sort((a, b) => a.index - b.index).forEach(item => { console.log(`任务 ${item.index} 创建完成:`, item.result); }); console.log('所有任务已完成'); } catch (error) { console.error('任务执行出错:', error); } })();
这种方式下,所有请求同时发起,执行效率更高,但插入顺序不确定,但每个任务的title都是正确的。最后可以通过排序结果,按索引顺序查看所有任务的完成情况。
内容的提问来源于stack exchange,提问作者Hoknimo

