JavaScript中Axios异步请求内字典(对象)键值无法更新的问题排查与解决
testData empty after running async operations, and how to fix it? I've written this code in my app:
import {getTestData,getUserData} from "./js/apiInteractorModule.js"; const TEST_NAMES = ['CM10']; var testData = {}; function populateTestData(){ return axios({ method: 'post', url: '/get-session-cookie' }).then(response => { // Get decrypted session data getUserData(response.data.uuid,response.data.id_token).then(response => { // Get user data with decrypted session data (TODO: integrate with test data) for (let i = 0; i < TEST_NAMES.length; i++){ getTestData(TEST_NAMES[i]).then(response => { testData[TEST_NAMES[i]] = []; for (var key in response.json){ // Build data in format {name: [{x: time, y: value}]} (TODO: adjust with user data) testData[TEST_NAMES[i]].push({ x: response.json[key].time, y: response.json[key].value }); } }); } }) return response; }).catch(error => { console.log(error); return error; }); }
The getUserData and getTestData functions interact with external APIs. The getTestData API returns data like:
{success: true/false, json: {stringKey: {time: UNIX timestamp, value: float}}}
When I run populateTestData().then(response => { console.log(testData); });, the console shows testData is empty, and console.log(testData["CM10"]) returns undefined—even though I expect it to be {CM10: [{x: time, y: value}]} matching the API response.
As a test, if I initialize testData outside the function as var testData = {"CM10": 10}, then console.log(testData["CM10"]) outputs 10 correctly after running the same code.
Why is this happening, and how can I fix it?
What's causing this?
The core issue here is asynchronous timing. Let's break it down simply:
- Every API call you're making (
axios.post,getUserData,getTestData) is asynchronous—they run in the background while your main code keeps going. - When you call
populateTestData().then(...), the outerthentriggers as soon as the initial cookie request finishes. But at that moment, thegetUserDatacall inside it is still running, and none of thegetTestDatacallbacks that filltestDatahave executed yet. - So when you
console.log(testData)in that outerthen, the data hasn't been added to the object yet. That's why it's empty.
Your test with var testData = {"CM10": 10} works because that value is set immediately (synchronously)—no waiting required.
How to fix it
We need to make sure we wait for all asynchronous operations to finish before trying to access testData. The cleanest way to do this is using async/await (a more readable alternative to nested .then() calls) and Promise.all to handle parallel requests efficiently.
Here's the revised code:
import {getTestData, getUserData} from "./js/apiInteractorModule.js"; const TEST_NAMES = ['CM10']; var testData = {}; async function populateTestData() { try { // Wait for the initial cookie request to complete const cookieResponse = await axios({ method: 'post', url: '/get-session-cookie' }); // Wait for getUserData to finish before moving on const userData = await getUserData(cookieResponse.data.uuid, cookieResponse.data.id_token); // Wait for ALL getTestData requests to finish at the same time await Promise.all(TEST_NAMES.map(async (testName) => { const testResponse = await getTestData(testName); // Convert the API response to the format you need testData[testName] = Object.values(testResponse.json).map(item => ({ x: item.time, y: item.value })); })); // Now testData is fully populated return testData; } catch (error) { console.error(error); throw error; // Let the caller handle the error if needed } }
To use the fixed function:
populateTestData().then(data => { console.log(data); // Will show the fully populated testData console.log(data["CM10"]); // Works as expected now }).catch(error => { // Handle any errors here (e.g., API failures) });
Key improvements:
async/awaitmakes the code read like sequential, synchronous code, so you can clearly see when each operation finishes.Promise.allwaits for allgetTestDatarequests to complete in parallel (faster than running them one after another).- We return the populated
testDatadirectly from the function, so the caller can access it cleanly in the.then()block. Object.values()replaces thefor...inloop, making the code cleaner and more concise when converting the API response to your desired format.
内容的提问来源于stack exchange,提问作者M. Chak

