Async Axios请求未满足条件即执行:POST提前于GET运行问题
问题分析与解决:HubSpot接口调用顺序异常导致更新逻辑失效
问题场景
我有一个接收user对象的异步函数,逻辑是:
- 调用HubSpot的GET接口获取用户现有数据
- 对比返回数据与传入的
updatedUser,若不匹配则调用POST接口更新记录
但实际运行时,当传入对象与HubSpot原有记录不匹配时,POST请求似乎在GET请求完成前就执行了,导致GET拿到的是已经更新后的数据,if (!_.isEqual(hubspotData, updatedUser))分支的日志不打印,最终走了else分支。
代码示例
async function updateContactDetails(updatedUser) { const token = await verifyToken(); let hubspotData; // 获取现有记录 try { if (!token) throw new Error('Hubspot api token not found.'); const getUrl = `https://api.hubapi.com/contacts/v1/contact/email/${updatedUser.email}/profile`; const response = await axios.get(getUrl, { headers: { Authorization: `Bearer ${token}`, }, }); hubspotData = { firstname: response.data.properties?.firstname?.value, lastname: response.data.properties?.lastname?.value, email: response.data.properties?.email?.value, }; console.log('🚀 ~ updateContactDetails ~ updatedUser:', updatedUser); console.log('🚀 ~ updateContactDetails ~ hubspotData:', hubspotData); } catch (e) { console.log('Hubspot API - fetch error:', e?.response?.data || e); } // 对比后更新 if (!_.isEqual(hubspotData, updatedUser)) { console.log("not equal - making post request") try { console.log('🚀 ~ updateContactDetails ~ updating'); const updateUrl = `https://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/${updatedUser.email}`; const response = await axios.post( updateUrl, { properties: [ { property: 'firstname', value: updatedUser.firstname, }, { property: 'lastname', value: updatedUser.lastname, }, { property: 'email', value: updatedUser.email, }, ], }, { headers: { Authorization: `Bearer ${token}`, }, } ); const action = response.data.isNew ? 'create' : 'update'; console.log(`Contact ${action}d with ${response.config.data}`); } catch (e) { console.log('Hubspot API - create/update error:', e?.response?.data || e); } } else { console.log('Hubspot contact matches user input'); } }
控制台输出(异常情况)
传入firstname为Test6的updatedUser,HubSpot原有记录的firstname应为Test2,但输出显示:
🚀 ~ updateContactDetails ~ updatedUser: { firstname: 'Test6', lastname: 'Test2', email: <email>, } 🚀 ~ updateContactDetails ~ hubspotData: { firstname: 'Test6', lastname: 'Test2', email: <email>, } Hubspot contact matches user input
排查过程
- 尝试将所有代码放入同一个try/catch、移除所有try/catch、简化对比逻辑、抽象接口调用为独立函数,均无效。
- 注释POST请求代码后,对比逻辑正常触发,说明对比逻辑本身无问题;恢复POST后,更新操作立即执行,导致GET拿到更新后的数据。
核心原因
最可能的情况是函数被重复并发调用:同一个updatedUser(或同一email)触发了多次updateContactDetails调用,第一次调用的POST还在执行时,第二次调用的GET已经发起,此时HubSpot的数据已经被更新,导致对比相等。
解决方案
1. 添加请求锁机制,确保同一email的请求串行处理
用一个Map来记录正在处理的email,避免并发请求干扰:
// 全局维护请求锁 const contactUpdateLocks = new Map(); async function updateContactDetails(updatedUser) { // 检查当前email是否正在处理 if (contactUpdateLocks.has(updatedUser.email)) { console.log(`Skipping update for ${updatedUser.email} - already in progress`); return; } contactUpdateLocks.set(updatedUser.email, true); const token = await verifyToken(); let hubspotData; try { if (!token) throw new Error('Hubspot api token not found.'); const getUrl = `https://api.hubapi.com/contacts/v1/contact/email/${updatedUser.email}/profile`; const response = await axios.get(getUrl, { headers: { Authorization: `Bearer ${token}`, }, }); hubspotData = { firstname: response.data.properties?.firstname?.value, lastname: response.data.properties?.lastname?.value, email: response.data.properties?.email?.value, }; console.log('🚀 ~ updateContactDetails ~ updatedUser:', updatedUser); console.log('🚀 ~ updateContactDetails ~ hubspotData:', hubspotData); if (!_.isEqual(hubspotData, updatedUser)) { console.log("not equal - making post request") console.log('🚀 ~ updateContactDetails ~ updating'); const updateUrl = `https://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/${updatedUser.email}`; const response = await axios.post( updateUrl, { properties: [ { property: 'firstname', value: updatedUser.firstname, }, { property: 'lastname', value: updatedUser.lastname, }, { property: 'email', value: updatedUser.email, }, ], }, { headers: { Authorization: `Bearer ${token}`, }, } ); const action = response.data.isNew ? 'create' : 'update'; console.log(`Contact ${action}d with ${response.config.data}`); } else { console.log('Hubspot contact matches user input'); } } catch (e) { if (e.message.includes('token not found')) { console.log('Hubspot API - token error:', e); } else if (e.response?.data) { console.log('Hubspot API - error:', e.response.data); } else { console.log('Hubspot API - unexpected error:', e); } } finally { // 释放锁 contactUpdateLocks.delete(updatedUser.email); } }
2. 检查函数调用时机
- 前端场景:确认按钮点击、表单提交等触发函数的事件是否被重复绑定,导致多次调用。
- 后端场景:检查是否有定时任务、webhook等重复触发该函数。
3. 验证执行顺序(调试用)
在GET和POST请求前后添加时间戳日志,确认实际执行顺序:
// 在GET请求前添加 console.log(`[${new Date().toISOString()}] Starting GET for ${updatedUser.email}`); // GET请求后添加 console.log(`[${new Date().toISOString()}] Completed GET for ${updatedUser.email}`); // POST请求前添加 console.log(`[${new Date().toISOString()}] Starting POST for ${updatedUser.email}`); // POST请求后添加 console.log(`[${new Date().toISOString()}] Completed POST for ${updatedUser.email}`);
总结
这种异步流程异常通常是并发调用导致的资源竞争,通过添加请求锁确保同一资源的操作串行化,就能解决GET和POST的顺序干扰问题。
内容的提问来源于stack exchange,提问作者o-mac
相关产品推荐
相关产品推荐

