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

Node.js中异步代码顺序调换为何引发Promise无法resolve?

Node.js连接顺序导致Promise无法resolve问题分析

问题场景

我尝试以不同顺序执行两个连接操作:

  1. 连接WebSocket客户端到WebSocket服务器
  2. 连接套接字客户端到Unix域套接字服务器

三种测试情况:

  • promise1:先连WebSocket,再连Unix套接字 → 始终无法resolve
  • promise2:先连Unix套接字,再连WebSocket → 正常resolve
  • promise3:并行处理两个连接 → 正常resolve

测试代码:

import * as net from 'node:net';
import fs from 'fs';
import WebSocket, { WebSocketServer } from 'ws';

const testWSPort = Math.round(Math.random() * 10000);
const testSocketPath = `/tmp/test-${testWSPort}.sock`;

(async () => {
  if (fs.existsSync(testSocketPath)) {
    fs.unlinkSync(testSocketPath);
  }

  await startWSS({ port: testWSPort });
  (await startUnixDomainSocketServer(testSocketPath)).server;
  console.time('wsClient');
  const wsClient = new WebSocket(`ws://localhost:${testWSPort}`);
  console.time('socketClient');
  const socketClient = net.createConnection({ path: testSocketPath });

  const promise1 = new Promise<void>(async (resolve, reject) => {
    await new Promise<void>((done) => {
      wsClient.on('open', () => {
        done();
      });
      wsClient.on('error', done);
    });

    await new Promise<void>((done) => {
      socketClient.on('connect', done);
      socketClient.on('error', done);
      socketClient.on('timeout', done);
    });

    resolve();
  });

  const promise2 = new Promise<void>(async (resolve) => {
    await new Promise<void>((done) => {
      socketClient.on('connect', done);
      socketClient.on('error', done);
      socketClient.on('timeout', done);
    });

    await new Promise<void>((done) => {
      wsClient.on('open', done);
      wsClient.on('error', done);
    });

    resolve();
  });

  const promise3 = Promise.all([
    new Promise<void>((done) => {
      socketClient.on('connect', () => {
        console.timeEnd('socketClient');
        done();
      });
      socketClient.on('error', done);
      socketClient.on('timeout', done);
    }),

    new Promise<void>((done) => {
      wsClient.on('open', () => {
        console.timeEnd('wsClient');
        done();
      });
      wsClient.on('error', done);
    }),
  ]);

  promise1.then(() => {
    console.log('Why does this line never executed?');
  });
  promise2.then(() => {
    console.log('But this line executed!');
  });
  promise3.then(() => {
    console.log('This line also executed');
  });
})();

function startWSS({ port }: { port: number }) {
  const wss = new WebSocketServer({ port });
  wss.on('connection', function connection(ws) {
    ws.on('error', console.error);
  });

  return new Promise<WebSocketServer>((resolve, reject) => {
    wss.on('listening', () => {
      resolve(wss);
    });

    wss.on('error', reject);
  });
}

function startUnixDomainSocketServer(
  socketPath: string
): Promise<{ server: net.Server }> {
  return new Promise((resolve, reject) => {
    const server = new net.Server();

    server.listen(socketPath, () => {
      resolve({ server });
    });

    server.on('error', (err) => {
      reject(err);
    });
  });
}

核心问题

  1. 为什么promise1始终无法resolve?
  2. 为何调换连接顺序会产生这种差异?

问题分析

1. promise1无法resolve的原因

核心问题是事件绑定时机晚于事件触发时机:

  • Unix域套接字的连接建立速度远快于WebSocket(前者是本地IPC,无TCP握手;后者需要完成HTTP握手流程)。
  • 创建socketClient后,它会立即发起连接,且连接会在极短时间内完成,connect事件会在当前同步代码执行完毕后进入事件队列等待处理。
  • 在promise1的逻辑中,你先等待WebSocket的open事件完成,之后才给socketClient绑定connect事件。但此时socketClient的connect事件已经触发过了——Node.js的事件是一次性的(触发后不会重复发射),新绑定的回调永远不会被调用,导致第二个await的Promise永远处于pending状态,promise1也就无法resolve。

2. 调换顺序后正常的原因

在promise2中,你先给socketClient绑定connect事件:

  • 此时socketClient的连接尚未完成(同步代码还在执行,事件队列未处理),绑定的回调能正常捕获到后续触发的connect事件。
  • 当socketClient的连接完成后,再给wsClient绑定open事件——此时WebSocket的握手流程还未完成,open事件尚未触发,因此后续能正常捕获到该事件,最终promise2可以正常resolve。

补充:promise3正常的原因

promise3通过Promise.all并行绑定两个事件:

  • 在同步代码阶段就完成了两个客户端的事件绑定,此时两个连接都尚未完成,后续事件触发时都能被对应的回调捕获,因此可以正常resolve。

内容的提问来源于stack exchange,提问作者Shu Chang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:55:56