MST

星途 面试题库

面试题:JavaScript在Node编程中的事件循环与异步处理

在Node.js环境下,描述事件循环的机制以及它是如何处理异步I/O操作、定时器等任务的。结合Promise、async/await 说明如何优雅地管理异步流程,避免回调地狱。
30.5万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

事件循环机制

  1. 概述:Node.js 是单线程的,事件循环是其实现异步操作的核心机制。它允许 Node.js 在非阻塞的方式下处理 I/O 操作,通过循环不断检查事件队列,处理其中的任务。
  2. 事件循环阶段
    • timers:这个阶段执行 setTimeoutsetInterval 设定的回调函数。
    • I/O callbacks:处理一些系统级别的回调,比如 TCP 连接错误等。
    • idle, prepare:仅内部使用。
    • poll:这是事件循环中最重要的阶段,在这个阶段会执行以下操作:
      • 如果有定时器到期,会执行定时器回调,进入 timers 阶段。
      • 如果有新的 I/O 事件,会执行对应的 I/O 回调。
      • 如果事件队列中没有待处理事件,且没有定时器设定,Node.js 会在此阶段阻塞等待新事件。
    • check:执行 setImmediate 设定的回调函数。
    • close callbacks:执行一些关闭的回调,比如 socket.on('close', ...)

处理异步 I/O 操作和定时器

  1. 异步 I/O 操作:当 Node.js 执行到异步 I/O 操作(如文件读取、网络请求)时,它不会阻塞线程等待操作完成。而是将该操作交给底层的 I/O 线程池(在 Node.js 中,不同的 I/O 操作可能使用不同的线程池)去处理,然后继续执行事件循环中的其他任务。当 I/O 操作完成后,将对应的回调函数放入事件队列,等待事件循环处理。
  2. 定时器setTimeoutsetInterval 会将回调函数放入 timers 队列。在事件循环的 timers 阶段,会检查定时器是否到期,如果到期则将回调函数放入事件队列等待执行。不过,定时器的执行时间并不是绝对精确的,因为事件循环只有在到达 timers 阶段才会检查定时器,并且如果前面的阶段执行时间过长,可能会导致定时器回调延迟执行。

使用 Promise、async/await 管理异步流程

  1. Promise
    • 基本概念:Promise 是一个代表异步操作最终完成(或失败)及其结果的对象。它有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。一旦状态改变,就不会再变。
    • 解决回调地狱:通过链式调用 .then() 方法来处理异步操作的成功和失败情况,避免了层层嵌套的回调函数。例如:
function asyncOperation() {
    return new Promise((resolve, reject) => {
        // 模拟异步操作
        setTimeout(() => {
            resolve('Operation completed');
        }, 1000);
    });
}

asyncOperation()
  .then(result => {
        console.log(result);
        return anotherAsyncOperation();
    })
  .then(anotherResult => {
        console.log(anotherResult);
    })
  .catch(error => {
        console.error(error);
    });
  1. async/await
    • 基本概念async 函数是一种异步函数,它返回一个 Promise 对象。await 关键字只能在 async 函数内部使用,它会暂停 async 函数的执行,等待 Promise 被解决(resolved 或 rejected),然后恢复 async 函数的执行,并返回 Promise 的解决值。
    • 解决回调地狱async/await 使得异步代码看起来像同步代码,极大地提高了代码的可读性。例如:
async function main() {
    try {
        const result1 = await asyncOperation();
        console.log(result1);
        const result2 = await anotherAsyncOperation();
        console.log(result2);
    } catch (error) {
        console.error(error);
    }
}

main();

通过使用 Promise 和 async/await,我们可以将异步操作以一种更优雅、更易于理解和维护的方式进行管理,有效避免了回调地狱的问题。