Node.js中如何同步不同模块中无返回值的函数?
问题:Node.js批量处理数据时因异步操作导致超时
我是Node.js新手,首次提问,表述不当请见谅。main.js处理小范围数据正常,但处理大范围数据时会超时,原因是数据获取与插入操作不同步。流程如下:
- main.js通过for循环调用fetchPincode.js中的fetchPincode函数
- 该函数使用
https.get从指定URL提取数据 - 提取的数据通过insertMany.js中的insertMany函数批量插入MongoDB
我希望insertMany能等待https.get完成后再执行,实现同步但未能成功,相关代码如下:
main.js
const fetchPincode = require('./fetchPincode.js') const start_pincode = 110001 const end_pincode = 110097 for(let i=start_pincode;i<=end_pincode;i++) { // console.log('state.js '+i) fetchPincode.fetchPincode(i) }
fetchPincode.js
require('dotenv').config() const https = require('https') const insertMany = require('./insertMany.js') function fetchPincode(pincode) { https.get(process.env.PINCODE_URL+pincode,(res)=>{ let data = '' res.on('data',(d)=>{ data += [d] if (JSON.parse(data)[0].PostOffice != null) { insertMany.insertMany(JSON.parse(data)[0].PostOffice,pincode) // console.log(JSON.parse(data)[0].PostOffice) } }) }) } module.exports = {fetchPincode}
insertMany.js
const MongoClient = require('mongodb').MongoClient function insertMany(data,pincode) { console.log(data) MongoClient.connect(process.env.MO_POSTAL,(error,db)=>{ if (error) throw error // console.log('==========> MongoDB Connected') db.db('POSTAL').collection('POSTOFFICE').insertMany(data,(error,res)=>{ if (error) throw error console.log(`For pincode: ${pincode}, ${res.insertedCount} rows inserted`) db.close() }) }) } module.exports = {insertMany}
.env
PINCODE_URL='https://api.postalpincode.in/pincode/' MO_POSTAL='mongodb+srv://abcd:abcd@cluster0.abcd.mongodb.net/POSTAL?keepAlive=true&socketTimeoutMS=2100000000&connectTimeoutMS=2100000000&serverSelectionTimeoutMS=2100000000'
解决方案
问题根源
https.get的数据接收是分段的,你在data事件中直接解析完整JSON,可能数据还没接收完就触发解析,导致错误或不完整操作- 循环中一次性发起所有请求,并发过高,超出API或MongoDB的处理能力,引发超时
- 所有异步操作未通过Promise/async-await控制顺序,无法保证
insertMany在数据获取完成后执行
修改后的代码
1. fetchPincode.js(Promise化+等待数据接收完成)
require('dotenv').config() const https = require('https') const insertMany = require('./insertMany.js') function fetchPincode(pincode) { return new Promise((resolve, reject) => { https.get(process.env.PINCODE_URL + pincode, (res) => { let data = '' // 分段接收数据,累加即可 res.on('data', (d) => { data += d }) // 数据接收完成后再处理 res.on('end', () => { try { const result = JSON.parse(data)[0] if (result.PostOffice) { // 等待插入操作完成再标记当前请求处理完毕 return insertMany.insertMany(result.PostOffice, pincode) .then(resolve) .catch(reject) } resolve() } catch (err) { reject(new Error(`解析${pincode}数据失败:${err.message}`)) } }) // 监听请求错误 res.on('error', (err) => reject(new Error(`获取${pincode}数据失败:${err.message}`))) }).on('error', (err) => reject(new Error(`发起${pincode}请求失败:${err.message}`))) }) } module.exports = { fetchPincode }
2. insertMany.js(Promise化操作)
const MongoClient = require('mongodb').MongoClient function insertMany(data, pincode) { return new Promise((resolve, reject) => { MongoClient.connect(process.env.MO_POSTAL, (error, client) => { if (error) { return reject(new Error(`连接MongoDB失败:${error.message}`)) } client.db('POSTAL').collection('POSTOFFICE').insertMany(data, (error, res) => { // 无论成功失败都关闭连接 client.close() if (error) { return reject(new Error(`插入${pincode}数据失败:${error.message}`)) } console.log(`For pincode: ${pincode}, ${res.insertedCount} rows inserted`) resolve(res) }) }) }) } module.exports = { insertMany }
3. main.js(串行控制并发)
const fetchPincode = require('./fetchPincode.js') const start_pincode = 110001 const end_pincode = 110097 // 用async-await实现串行处理,避免并发过高 async function processPincodes() { for (let i = start_pincode; i <= end_pincode; i++) { try { await fetchPincode.fetchPincode(i) console.log(`✅ 处理完成:${i}`) } catch (err) { console.error(`❌ 处理${i}失败:`, err.message) // 可选:遇到错误是否继续处理后续邮编,这里选择继续 continue } } console.log('所有邮编处理完成') } processPincodes()
额外优化建议
- 如果需要更高效率,可以用并发控制库(如
p-limit)限制同时处理的请求数(比如一次处理5个),避免串行太慢 - 可以复用MongoDB连接,不用每次插入都重新连接,提升性能
- 给API请求添加超时时间,避免长时间无响应
内容的提问来源于stack exchange,提问作者DevX
相关产品推荐
相关产品推荐

