Node.js中常用的异步编程方法

为什么会出现异步(多线程能力的Node.js)
  • Node.js 有一个主线程负责执行 JavaScript 代码。
  • I/O 操作(文件读写、网络请求等)被【委托给系统底层】。
事件循环 (Event Loop)
  1. JavaScript 代码是在一个主线程中顺序执行的,这个主线程运行着事件循环机制。

  2. 执行一些异步I/O操作时为了不阻塞主线程,这些任务被提交到了libuv的线程池中由后台线程执行。 当异步操作完成,它将产生一个事件或回调函数。事件循环监听这些事件或回调函数,然后将它们推入回调队列中,等待主线程空闲时处理。

看看下面这个段代码的输出结果

js 复制代码
const fs = require('fs');

console.log('Start');

//宏任务
setTimeout(() => {
  console.log('Timeout');
}, 0);
//异步回调
fs.readFile(__filename, () => {
  console.log('Read File');
});
//宏任务
setImmediate(() => {
  console.log('Immediate');
});
//微任务
process.nextTick(() => {
  console.log('Next Tick');
});

console.log('End');

得到的输出结果如下:

js 复制代码
Start
End
Next Tick
Timeout
Immediate
Read File
常用的异步编程机制

在 Node.js 中,异步编程有多种实现方式,包括回调函数Promise事件监听async/await

回调函数

回调函数在特定条件满足时(如事件触发、异步操作完成)被调用的, 即在某个操作完成后被调用的函数。它是异步编程的核心。

js 复制代码
const fs = require('fs'); 
fs.readFile('file.txt', 'utf8',(err, data) => {
if (err) { 
console.error(err); 
return;
}
console.log(data);
});

优点:

  • 简单易懂,容易上手。
  • 可以处理简单的异步操作。

缺点:

  • 回调地狱:当有多个异步操作需要依次执行时,代码会变得混乱和难以维护。
  • 错误处理困难:如果一个回调函数中发生错误,很难捕获和处理这个错误。
Promise

Promise 对象表示一个异步操作的最终完成结果或失败结果, Node.js 也有基于Promise 的fs/promises模块。

以下为使用fs/promises执行读取文件的操作示例:

js 复制代码
const fs = require('fs').promises;

fs.readFile('file.txt', 'utf8')
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.error(err);
  });

优点:

  • 可以避免多层嵌套的回调函数,使代码更加清晰和易于维。
  • 可以通过链式调用.then().catch()方法来处理复杂的异步流程。

缺点:

  • 无法取消promise,一旦新建他就会立即执行,无法中途取消。
  • 如果不设置回调函数,promise内部抛出的错误,不会反应到外部。
  • 当处于pending状态时,无法得知目前的状态是刚刚开始还是即将完成。
async/await

async/await基于Promise的语法糖。

async关键字用于定义一个异步函数,这类函数始终会返回一个 Promise 对象。 而await关键字则只能在async函数内部使用,它的作用是等待一个 Promise 对象的状态变为 fulfilledrejected,并暂停async函数的执行,直到所等待的 Promise 被解决。

async/await它允许我们以同步的方式编写异步代码,同时保留异步操作的非阻塞特性。

看看示例:

js 复制代码
const fs = require('fs').promises;

async function readFile() {
  try {
    const data = await fs.readFile('file.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

readFile();

优点:

  • 消除回调地狱,扁平化代码结构。
  • 错误处理使用try/catch 比Promise 的链式调用可读性高。

缺点:

  • 只能用于异步函数中,不能用于普通函数中。
  • 不能处理所有类型的异步操作,例如定时器和事件。
  • 可能会导致性能问题,因为它会创建额外的 Promise 对象和执行上下文。
发布订阅模式
  • 多线程机制(Web Worker/worker_threads )使用的postMeassage/onmessage。
  • 使用Node.js内置的events模块,如下HTTP请求 server on('request') 事件监听。
js 复制代码
const EventEmitter = require("events");

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

myEmitter.on("event", () => {
  console.log("an event occurred!");
});
myEmitter.emit("event");

const http = require("http");

const server = http.createServer((req, res) => {
  res.end("hello!!! this is YK!!!");
});
server.on("request", (req, res) => {
  console.log(req.url);
});

server.listen(3000);
相关推荐
cyadyx26 分钟前
Vite 比 Webpack 构建效率更高
前端·webpack·node.js·vite
mCell2 小时前
AI 时代 SVG 画图的潜力
前端·agent·svg
我命由我123458 小时前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
Hopebearer_9 小时前
页面突然只剩 DOM?一次静态资源版本错配排查
前端·部署
东风破_9 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
圣殿骑士-Khtangc11 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
BigTopOne11 小时前
【ijkplayer】 硬解码流程
前端
kyriewen12 小时前
DeepSeek Harness开源第一天我就上手了——和Claude Code的差距比想象中大
前端·ai编程·deepseek
捡田螺的小男孩12 小时前
什么是 Skill?手把手带你写一个简单有用的 Skill!
前端·后端·程序员
IT_陈寒12 小时前
Redis集群这个坑,差点让我通宵
前端·人工智能·后端