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

Node.js同步执行问题求助:Express中脚本与函数需按序执行

Fixing Async Order in Your Express Route

Hey there! I totally get the frustration with JavaScript's async behavior when you're just starting out—let's walk through exactly what's breaking your sequence and fix it with clear, manageable changes.

What's Going Wrong Right Now

Your current code runs all three steps almost at the same time, not in order:

  1. You call shell.exec(commandString) but don't wait for the bash script to finish running. That means when readFolder() runs, the script might not have generated the report files yet.
  2. Inside readFolder(), fs.readdir is asynchronous—it starts reading the folder but immediately moves on to renderPage() before it finishes loading the files. So ipAddressesLink and ipAddresses are still empty when you try to render the page.
  3. You're calling res.end() before renderPage() can even send the response, which cuts off the request early.

The Fix: Use Async/Await (Simplest for Beginners)

Async/await lets you write asynchronous code that reads like synchronous code, making it way easier to control the order of operations. Here's how to adjust your code step by step:

Step 1: Convert the Route Handler to Async

First, mark your route callback as async—this lets you use await inside it.

Step 2: Wrap Shell Exec in a Promise

Most older Node.js APIs use callbacks, but we can wrap them in Promises to use await. Let's create a helper function for running your shell command.

Step 3: Use Promise-Based File Reading

Node.js has a promise-based version of fs (called fs.promises) that works great with await, so we'll use that instead of the callback version of fs.readdir.

Step 4: Remove Global Variables (Avoid Race Conditions)

Using global variables for ipAddressesLink and ipAddresses is risky—if two users send requests at the same time, their data will overwrite each other. We'll make them local variables instead.

Full Modified Code

// Make sure to require fs at the top if you haven't already
const fs = require('fs');
const shell = require('shelljs'); // Assuming you're using shelljs for shell.exec

//Run script when post is rec'd from root and send to results page
app.post("/", async (req, res) => {
  try {
    // Helper function to run shell command and return a Promise
    const runShellCommand = (command) => {
      return new Promise((resolve, reject) => {
        shell.exec(command, (code, stdout, stderr) => {
          if (code !== 0) {
            // Reject if the script fails (so we can catch the error)
            reject(new Error(`Script failed with exit code ${code}: ${stderr}`));
          } else {
            // Resolve if the script succeeds
            resolve(stdout);
          }
        });
      });
    };

    // Take values and create complete command for Astrum script
    const commandString = `bash /home/astrum/Main/Astrum.sh -s ${req.body.speed} -h ${req.body.host} -u ${req.body.username} -p ${req.body.password}`;
    
    // 1. Wait for the shell script to finish executing
    await runShellCommand(commandString);

    // 2. Wait for the folder to be read (using promise-based fs)
    const files = await fs.promises.readdir('./reports/html/');

    // Function to remove last five characters of each element
    const removeExtension = (value) => {
      return value.substring(0, value.length - 5);
    };

    // Process the file list (local variables, not global!)
    const ipAddressesLink = files;
    const ipAddresses = files.map(removeExtension);

    // 3. Render the page (now with all data ready!)
    res.render("results", { ipAddressesLink, ipAddresses, title: 'Results' });
  } catch (error) {
    // Handle any errors (script failure, folder read error, etc.)
    console.error('Error:', error);
    res.status(500).send('Something went wrong while processing your request');
  }
});

Key Changes Explained

  • async/await: Ensures each step waits for the previous one to finish before moving on. No more race conditions!
  • Promise Wrapper for shell.exec: Lets us wait for the bash script to complete and handle errors properly.
  • fs.promises.readdir: Replaces the callback-based fs.readdir so we can await the file list.
  • Local Variables: Eliminates the risk of concurrent requests overwriting each other's data.
  • Error Handling: The try/catch block catches any issues (like the script failing or the folder not existing) and sends a proper error response instead of crashing the server.
  • No res.end(): res.render automatically sends the response when it's done, so we don't need to call res.end() manually.

Quick Notes for Your Learning

  • Promises are objects that represent the eventual completion (or failure) of an async operation. await pauses execution until the Promise resolves.
  • Async Functions always return a Promise, even if you don't explicitly write one.
  • Never use global variables for request-specific data—it's a common source of bugs in Express apps.

内容的提问来源于stack exchange,提问作者Brett Johnson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:53:03