You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js Promise执行顺序问题求助:期望1,2,3输出却得1,3,2

Why Your Log Order Is 1, 3, 2 Instead of 1, 2, 3

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

  1. When you call await connect(device) in your route handler, the connect function does wait for device.connect() and the login promise to resolve. But once it hits c1.data.subscribe(...), that's an asynchronous event that runs outside the Promise chain you're awaiting.
  2. Your connect function doesn't return a Promise that waits for the subscribe callback to fire. Since async functions auto-wrap their return value in a Promise, but you're not waiting for the subscription to complete, connect resolves immediately after console.log(1) runs.
  3. This means your route handler jumps straight to console.log('3') before the subscribe callback (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 makes await pause until that data arrives.
  • The connect function now only resolves after the data is processed and console.log('2') runs.
  • With consistent async/await, the execution order becomes linear and predictable:
    1. console.log(1) runs after writing to the channel
    2. We wait for the observable to emit data, then run console.log('2')
    3. Only after all that does connect resolve, so the route handler runs console.log('3')

Quick Notes

  • If c1.data emits multiple values and you need to wait for the final one, use lastValueFrom instead of firstValueFrom.
  • Proper error handling in both the connect function and route handler prevents silent failures that are hard to debug.

内容的提问来源于stack exchange,提问作者Maicon Santos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:28:01