Node.js异步执行问题:S3配置加载后无法正确获取配置内容
Hey there, I see exactly what's going on here—this is a classic asynchronous JavaScript gotcha, totally common when you're starting out with Node.js!
The Root Problem
Your s3.getObject() call is asynchronous: it sends the request to S3 and immediately moves on to run the rest of your code (like subscriptions = cfg.subscriptions; and that final log statement) before the S3 response comes back. That's why your log shows the path first, then the actual config content later—by the time the config loads, the code that depends on it has already run.
Fixes: Let's Make Your Code Wait for the Config
There are two clean ways to fix this, and I'll show you both (with the modern, easier-to-read one first):
Option 1: Use async/await (Recommended)
Lambda supports async handlers out of the box, so this will make your code linear and easy to follow. We'll convert the callback-based S3 call to a promise and wait for it to finish before proceeding:
// Convert your Lambda handler to an async function exports.handler = async (event, context) => { let cfg = process.env.CONFIG_FILE_NAME; log.info("Loading config '%s'", cfg); // Simplify S3 path check with toLowerCase() if (cfg.toLowerCase().includes("s3")) { log.info("S3 path detected"); const s3 = new aws.S3(); const myRegex = /\/\/(\w*)\/(.*)/g; const matched = myRegex.exec(cfg); const bucket = matched[1]; const key = matched[2]; log.info("Extracted bucket: ", bucket); log.info("Extracted key: ", key); try { // Wait for S3 to return the object (use .promise() to convert callback to promise) const data = await s3.getObject({ Bucket: bucket, Key: key }).promise(); log.info("Loaded config from S3"); cfg = JSON.parse(data.Body); log.info("Config content: ", cfg); } catch (err) { log.warn(err, err.stack); throw err; // Let Lambda handle the error, or add custom handling here } } else { try { // Local config load (use async fs to keep consistency) const fs = require('fs').promises; const fileContent = await fs.readFile(cfg, 'utf8'); cfg = JSON.parse(fileContent); log.info("Loaded config from local file"); } catch (e) { log.warn("Failed to load local config: ", e); throw e; } } // Now cfg is definitely the parsed JSON object! subscriptions = cfg.subscriptions; log.info("This contains config content: ", cfg); // Rest of your code that depends on subscriptions goes here };
Option 2: Move Dependent Code Into the Callback
If you prefer to stick with callbacks (though async/await is cleaner), you just need to put all code that relies on cfg inside the S3 callback (and the local load try block):
var cfg = process.env.CONFIG_FILE_NAME; log.info("Loading config '%s'", cfg); if(cfg.indexOf("s3") !== -1 || cfg.indexOf("S3") !== -1) { log.info("S3 path detected"); var s3 = new aws.S3(); var myRegex = /\/\/(\w*)\/(.*)/g; var matched = myRegex.exec(cfg); var bucket = matched[1]; var key = matched[2]; log.info("Extracted bucket: ", bucket); log.info("Extracted key: ", key); var params = { Bucket: bucket, Key: key }; s3.getObject(params, function(err, data) { if (err) { log.warn(err, err.stack); return; } else { log.info("Loaded config from S3"); cfg = JSON.parse(data.Body); log.info("Config content: ", cfg); // ALL code that uses cfg/subscriptions goes HERE subscriptions = cfg.subscriptions; log.info("This contains json content from S3: ", cfg); // Rest of your logic dependent on subscriptions } }); } else { try { // Local config load code here cfg = JSON.parse(fs.readFileSync(cfg, 'utf8')); log.info("Loaded config from local file"); // Local-dependent code here subscriptions = cfg.subscriptions; log.info("This contains local config: ", cfg); } catch (e) { log.warn("Local config load error: ", e); } }
Key Takeaway
Node.js doesn't wait for asynchronous operations (like API calls or file reads) to finish before moving on. Any code that depends on the result of those operations needs to run after the operation completes—either by using async/await to pause execution, or by placing it inside the operation's callback.
内容的提问来源于stack exchange,提问作者dondon

