Why does switching the position of async code cause infinite execution in node.js?

63 Views Asked by At

In the code below, I try to

  • Connect a WebSocket client to a WebSocket server
  • Connect the socket client to a socket server with different orders.

In the promise1, the order of connection is:

  1. Connect a WebSocket client to a WebSocket server.
  2. Connect socket client to a socket server. And the promise1 never resolved.

In the promise2, the order of connection is:

  1. Connect socket client to a socket server.
  2. Connect a WebSocket client to a WebSocket server. And the promise2 is resolved.
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);
    });
  });
}

My questions

Why is promise1 never resolved?

Why change the position of this order cause this difference?

You can try this code here: https://stackblitz.com/edit/stackblitz-starters-ervgkg?file=index.ts

What I have tried

I think this is a problem related to node.js event loop. I read this article, but I still can't figure out the reason.

0

There are 0 best solutions below