JavaScript异步编程:Node.js完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

JavaScript 异步编程简介



JavaScript 是一种单线程编程语言,采用非阻塞事件驱动模型。在 Node.js 环境中,异步编程不仅是一种便利选项,更是基本需求。如果没有异步机制,服务器应用程序在每次数据库查询、文件读取或网络调用时都会被阻塞,使其无法用于实际工作。



在本教程中,我们将讲解 JavaScript 中异步编程的三种主要范式:Callback、Promise 和 async/await。您将了解 Event Loop 的工作原理,学会处理错误,并为 Node.js 编写可读、高效的代码。



1. Callback:异步的基础



Callback(回调函数)是作为参数传递给另一个函数,并在异步操作完成后被调用的函数。这是 JavaScript 中最古老、最基本的异步模型。



使用 Callback 的简单示例



const fs = require('fs');

// 使用 callback 异步读取文件fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('读取文件错误:', err); return; } console.log('文件内容:', data);});

console.log('文件读取已启动...');// 输出:"文件读取已启动..." 会出现在文件内容之前


在这个示例中,回调函数只有在文件完全读取后才会执行。与此同时,主线程继续执行——这就是非阻塞行为。



Callback Hell(回调地狱)



回调的主要缺点是在嵌套调用时迅速失去可读性:



// 回调地狱getUser(1, (err, user) => {  if (err) throw err;  getPosts(user.id, (err, posts) => {    if (err) throw err;    getComments(posts[0].id, (err, comments) => {      if (err) throw err;      console.log(user.name, posts[0].title, comments);    });  });});


这样的代码难以调试、维护和扩展。幸运的是,现代 JavaScript 提供了更优雅的解决方案。



2. Promise:结果的承诺



Promise(承诺)是一个表示异步操作结果的对象,该操作可能已完成(fulfilled)、被拒绝(rejected)或处于等待状态(pending)。Promise 可以避免嵌套并构建调用链。



创建和使用 Promise



// 返回 Promise 的函数function readFilePromise(filename) {  return new Promise((resolve, reject) => {    const fs = require('fs');    fs.readFile(filename, 'utf8', (err, data) => {      if (err) {        reject(err); // 错误——Promise 被拒绝      } else {        resolve(data); // 成功——Promise 已完成      }    });  });}

// 使用 PromisereadFilePromise('example.txt') .then(data => { console.log('内容:', data); return data.length; }) .then(length => { console.log('文本长度:', length); }) .catch(err => { console.error('错误:', err); });


Promise.all 和 Promise.race 方法



Promise.all 允许等待所有 Promise 完成,而 Promise.race 等待第一个完成的 Promise:



const p1 = readFilePromise('file1.txt');const p2 = readFilePromise('file2.txt');const p3 = readFilePromise('file3.txt');

// 等待所有三个文件Promise.all([p1, p2, p3]) .then(([data1, data2, data3]) => { console.log('所有文件已加载'); console.log(data1, data2, data3); }) .catch(err => console.error('其中一个文件出错:', err));

// 等待第一个完成的Promise.race([p1, p2, p3]) .then(firstResult => { console.log('第一个文件已加载:', firstResult); });


3. Async/Await:异步代码的同步风格

博客

书籍推荐