Node.js Promise执行顺序问题求助:期望1,2,3输出却得1,3,2
Hey there, let's break down exactly what's throwing off your execution order and fix it. The core issue is that you're mixing async/await with RxJS observables (the subscribe call) without properly waiting for the observable to finish processing data. Here's the breakdown:
The Root Cause
- When you call
await connect(device)in your route handler, theconnectfunction does wait fordevice.connect()and theloginpromise to resolve. But once it hitsc1.data.subscribe(...), that's an asynchronous event that runs outside the Promise chain you're awaiting. - Your
connectfunction doesn't return a Promise that waits for thesubscribecallback to fire. Sinceasyncfunctions auto-wrap their return value in a Promise, but you're not waiting for the subscription to complete,connectresolves immediately afterconsole.log(1)runs. - This means your route handler jumps straight to
console.log('3')before thesubscribecallback (which logs '2') ever gets triggered.
The Fix
We need to convert the RxJS observable into a Promise so we can await it, ensuring the entire flow waits for data processing to finish. Let's also clean up the code to use consistent async/await instead of mixing with .then() chains—it'll make the execution order way easier to follow.
First, make sure you have the right RxJS utility (use firstValueFrom/lastValueFrom for RxJS 7+; older versions can use toPromise(), though it's deprecated):
// Import if using ES modules (adjust based on your project setup) // import { firstValueFrom } from 'rxjs'; app.get('/', async (req, res) => { const device = new api('138.118.87.76', '1420'); try { const data = await connect(device); console.log('3'); res.json(data); } catch (err) { res.status(500).json({ error: 'Failed to fetch device data', details: err.message }); } }) async function connect(device){ try { // Await connection and login step-by-step const [login] = await device.connect(); const conn = await login(passwords.mikrotikEquipamentUser, passwords.mikrotikEquipament); const c1 = conn.openChannel(); c1.write('/ip/address/print'); console.log(1); // Convert observable to Promise to wait for data emission const rawData = await firstValueFrom(c1.data); console.log('2'); // Return processed data for the route handler return api.resultsToObj(rawData); } catch (err) { console.log("Error during processing:", err); // Re-throw error so the route handler can handle it throw err; } }
Why This Works
firstValueFrom(c1.data)turns the observable into a Promise that resolves when the first piece of data is emitted. This makesawaitpause until that data arrives.- The
connectfunction now only resolves after the data is processed andconsole.log('2')runs. - With consistent async/await, the execution order becomes linear and predictable:
console.log(1)runs after writing to the channel- We wait for the observable to emit data, then run
console.log('2') - Only after all that does
connectresolve, so the route handler runsconsole.log('3')
Quick Notes
- If
c1.dataemits multiple values and you need to wait for the final one, uselastValueFrominstead offirstValueFrom. - Proper error handling in both the
connectfunction and route handler prevents silent failures that are hard to debug.
内容的提问来源于stack exchange,提问作者Maicon Santos

