如何优雅覆盖Promise解析值?Node.js数据库脚本重构咨询
Great question—let’s fix those anti-patterns and make your code cleaner, more maintainable, and aligned with best practices. Here’s how to address each of your concerns step by step:
1. Override a Promise’s Return Value Without Anti-Patterns
You don’t need to wrap Promise.all in a new Promise constructor (that’s the unnecessary anti-pattern you’re noticing). Instead, simply return your db object directly in the .then() callback attached to Promise.all. The .then() method automatically wraps its return value in a resolved Promise, so this achieves exactly what you want:
// Replace this clunky wrapper: return new Promise((resolve, reject) => { Promise.all([promiseDrop, promiseCreate]) .then(() => resolve(db)) .catch(reject) }) // With this concise version: return Promise.all([promiseDrop, promiseCreate]).then(() => db)
This keeps your code flat, avoids redundant Promise creation, and still passes the db object down the chain.
2. Ignore promiseDrop Errors Gracefully
To suppress errors from the drop operation (like when tables don’t exist yet), add a .catch() handler directly to promiseDrop that swallows the error (or logs it for debugging if you want visibility):
const promiseDrop = db.raw(sqlDrop).catch(err => { // Optional: Log the error for debugging without breaking the flow console.debug("Drop tables failed (likely tables didn't exist):", err.message) })
Now, even if promiseDrop rejects, Promise.all will only fail if promiseCreate rejects—exactly the behavior you need for the "start clean" flow.
3. Full Refactored Code (With Async/Await for Maximum Clarity)
For even more readable code, switch to async/await instead of raw Promise chains. It eliminates nested .then() calls and makes asynchronous logic read like synchronous code, which is far easier to debug:
const dropAndCreateTables = async (db, startClean) => { if (!startClean) return db; const sqlDrop = fs.readFileSync('drop.sql').toString(); // Suppress drop errors and proceed with table creation await db.raw(sqlDrop).catch(err => { console.debug("Skipping drop error (tables may not exist):", err.message); }); const sqlCreate = fs.readFileSync('create.sql').toString(); await db.raw(sqlCreate); // Return db to pass it along the promise chain return db; }; // Updated main flow (you can also convert this to async/await!) initDB({ debug: false }) .then(db => dropAndCreateTables(db, START_CLEAN)) .then(db => insertData(db, DO_UPSERT)) .then(() => console.info(`\n${timestamp()} done`)) .catch(handleError); // Or go fully async/await for the main flow: const runDatabaseSetup = async () => { try { const db = await initDB({ debug: false }); await dropAndCreateTables(db, START_CLEAN); await insertData(db, DO_UPSERT); console.info(`\n${timestamp()} done`); } catch (err) { handleError(err); } }; runDatabaseSetup();
Key Improvements:
- No more Promise constructor anti-patterns
- No nested
.then()chains - Drop errors are gracefully handled (with optional logging)
- Code is linear and intuitive to follow
- Async/await reduces cognitive load for asynchronous logic
Bonus: Non-Blocking File Reads (Optional)
If you want to avoid blocking the Node.js event loop with fs.readFileSync, replace it with async file reads using fs.promises.readFile:
const dropAndCreateTables = async (db, startClean) => { if (!startClean) return db; const sqlDrop = await fs.promises.readFile('drop.sql', 'utf8'); await db.raw(sqlDrop).catch(err => console.debug("Drop error:", err.message)); const sqlCreate = await fs.promises.readFile('create.sql', 'utf8'); await db.raw(sqlCreate); return db; };
This makes your entire setup flow fully non-blocking, which is better practice for Node.js applications.
内容的提问来源于stack exchange,提问作者Drop

