watcher.on('add')事件未等待SQL查询完成问题求助
目录监听事件中SQL操作未等待导致重复执行的问题解决
我在实现目录监听功能时遇到一个问题:当添加包含多个子文件夹和文件的目录时,watcher.on('add')事件触发后,在创建首个对应主文件夹的Job(将路径存入数据库)的过程中,日志打印“3”后整个流程就重复执行,没有等待首个Job的SQL创建操作完成。我希望事件能等待SQL查询完成后再继续执行,以下是我的代码:
watcher.on('add', async (filePath) => { const relativePath = path.relative(directoryToWatch, filePath); const folder = path.dirname(relativePath).split(path.sep)[0]; console.log(folder); console.log("1"); try { let folderExist = folderJobsList.find((item) => item.folder === folder); console.log(folderExist); console.log("2"); if (!folderExist) { console.log("3"); // Use a Promise to wait for the job creation job = await new Promise(async (resolve, reject) => { try { const createdJob = await JobModel.create({ folder, status: 'pending', }); console.log("4"); folderJobsList.push({ folder }); console.log("5"); console.log("*********"); console.log(JSON.stringify(folderJobsList)); console.log("*********"); console.log(JSON.stringify(createdJob)); console.log("---------"); resolve(createdJob); } catch (error) { reject(error); } }); console.log("6"); const file = await FileModel.create({ filename: filePath, relativePath: filePath, status: 'pending', jobId: job.id, }); console.log('File added to the database:', file); console.log("7"); } } catch (error) { console.error('Error creating File:', error); } })
问题分析
核心问题有两个:
- 不必要的Promise嵌套:你用
new Promise(async (...))包裹了本身就返回Promise的JobModel.create,这是异步反模式,会破坏Promise链的错误处理逻辑,还可能导致异步流程失控。 - 并发事件未串行化:当多个文件/文件夹同时触发
add事件时,由于JobModel.create是异步操作,folderJobsList还没来得及更新,后续事件会误以为文件夹不存在,重复执行Job创建流程。
解决方案
下面是修改后的代码,解决了上述问题:
// 新增:用于跟踪正在处理的文件夹,避免并发创建Job const pendingFolderJobs = {}; watcher.on('add', async (filePath) => { const relativePath = path.relative(directoryToWatch, filePath); const folder = path.dirname(relativePath).split(path.sep)[0]; console.log(folder); console.log("1"); try { let folderExist = folderJobsList.find(item => item.folder === folder); console.log(folderExist); console.log("2"); if (!folderExist) { console.log("3"); // 如果当前文件夹正在处理,等待之前的操作完成 if (pendingFolderJobs[folder]) { await pendingFolderJobs[folder]; // 重新检查文件夹是否已存在 folderExist = folderJobsList.find(item => item.folder === folder); if (folderExist) { // 直接创建文件记录 const file = await FileModel.create({ filename: filePath, relativePath: filePath, status: 'pending', jobId: folderExist.jobId }); console.log('File added to the database:', file); console.log("7"); return; } } // 标记当前文件夹正在处理 const jobPromise = (async () => { const createdJob = await JobModel.create({ folder, status: 'pending', }); console.log("4"); // 存入jobId,后续创建文件需要用到 folderJobsList.push({ folder, jobId: createdJob.id }); console.log("5"); console.log("*********"); console.log(JSON.stringify(folderJobsList)); console.log("*********"); console.log(JSON.stringify(createdJob)); console.log("---------"); return createdJob; })(); pendingFolderJobs[folder] = jobPromise; try { const job = await jobPromise; console.log("6"); const file = await FileModel.create({ filename: filePath, relativePath: filePath, status: 'pending', jobId: job.id, }); console.log('File added to the database:', file); console.log("7"); } finally { // 无论成功失败,都移除处理标记 delete pendingFolderJobs[folder]; } } else { // 文件夹已存在,直接创建文件记录 const file = await FileModel.create({ filename: filePath, relativePath: filePath, status: 'pending', jobId: folderExist.jobId }); console.log('File added to the database:', file); console.log("7"); } } catch (error) { console.error('Error creating File:', error); // 出错时清理处理标记,避免后续请求阻塞 if (pendingFolderJobs[folder]) { delete pendingFolderJobs[folder]; } } });
关键修改点
- 新增
pendingFolderJobs对象:跟踪正在处理的文件夹,同一文件夹的并发事件会等待前一个操作完成,避免重复创建Job。 - 移除Promise嵌套:直接
await JobModel.create返回的Promise,简化代码同时保证异步流程的正确性。 - 完善
folderJobsList存储:存入jobId,后续创建文件时可以直接关联到对应的Job。 finally块清理:确保无论操作成功还是失败,都移除处理标记,避免内存泄漏和后续请求阻塞。
内容的提问来源于stack exchange,提问作者Jassem Ben Rayana
相关产品推荐
相关产品推荐

