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);
相关推荐
周帝15 小时前
手写一个最简单版本的canvas库
前端
AAA简单玩转程序设计15 小时前
谁说Java枚举只是“常量装盒”?它藏着这些骚操作
java·前端
前端小蜗15 小时前
💰该省省,该花花!我靠白嫖飞书,把“每日生存成本”打了下来
前端·程序员·产品
YaeZed15 小时前
Vue3-父子组件通信
前端·vue.js
优爱蛋白15 小时前
IL-21:后Th1/Th2时代的免疫新星
java·服务器·前端·人工智能·健康医疗
Mintopia15 小时前
💬 从猜想到架构:AI 聊天区域的 Web 设计之道
前端·前端框架·aigc
一过菜只因15 小时前
VUE快速入门
前端·javascript·vue.js
滴滴答答哒15 小时前
Quartz Cron 表达式参考表
前端·css·css3
匠心网络科技15 小时前
前端学习手册-JavaScript条件判断语句全解析(十八)
开发语言·前端·javascript·学习·ecmascript