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);
相关推荐
IT_陈寒24 分钟前
Vite的public文件夹放静态资源?这坑我替你踩了
前端·人工智能·后端
涵涵(互关)37 分钟前
GoView各项目文件中的相关语法2
前端·javascript·vue.js
子兮曰44 分钟前
别让爬虫白嫖你的导航站了:纯免费,手把手实现加密字体防爬
前端·javascript·后端
小村儿1 小时前
连载06 - Hooks 源码深度解析:Claude Code 的确定性自动化体系
前端·后端·ai编程
心中无石马1 小时前
uniapp引入tailwindcss4.x
前端·css·uni-app
焰火19992 小时前
[Vue]可重置的响应式状态reactive
前端·vue.js
陆枫Larry2 小时前
CSS transform scale:图片放大效果背后的原理
前端
老王以为2 小时前
为什么 React 和 Vue 不一样?
前端·vue.js·react.js
web打印社区2 小时前
2026最新Web静默打印解决方案,无插件无预览,完美替代Lodop
前端·javascript·vue.js·electron·pdf
这个DBA有点耶2 小时前
分组排名不用窗口函数?那你还在写几十行的子查询
前端·代码规范