一、回调函数
回调函数是jsasync最基本的异步编程实现方式之一,它的核心思想是将一个函数作为另一个函数的参数传入,当另一个函数执行完毕后,再调用该函数。回调函数能够最大化利用单线程的优势,避免请求阻塞,使程序更加稳定和高效。
function fetchData(callback) {
setTimeout(() => {
const data = { name: "John", age: 30 };
callback(data);
}, 1000);
}
function showData(data) {
console.log(`Name: ${data.name}, Age: ${data.age}`);
}
fetchData(showData);
代码解析:
上述代码中的fetchData函数通过setTimeout模拟了一个异步操作,在1秒后返回一个对象。showData函数是回调函数,作为fetchData函数的参数传入,当fetchData函数运行完毕后,自动执行showData函数。这里展示了回调函数直接传参的实现,也可以使用匿名函数的方式来组合异步操作。
优点:简单易用,不需要额外安装模块,容易扩展。
缺点:回调嵌套会导致代码可读性低,异常处理不方便,容易产生回调地狱问题。
二、Promise
Promise是ES6规范中新增的异步编程方案,与回调函数相比,Promise具有更好的可读性和代码组织性,更容易处理异常与错误。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
fetchData()
.then(data => console.log(`Name: ${data.name}, Age: ${data.age}`))
.catch(error => console.error(error));
代码解析:
上述代码中的fetchData函数返回一个Promise对象,并在异步操作执行完成时调用resolve函数,传递数据作为参数。在使用时,可以将fetchData函数返回的Promise对象链式调用then方法,处理异步操作返回的结果。catch方法可以捕获Promise中的异常。
优点:结构清晰,避免了回调地狱的问题,可以链式调用。
缺点:需要编写较多的代码,同时理解Promise的工作机制也需要一定的学习成本。
三、Async/Await
Async/Await是Promise的语法糖,以同步代码的方式处理异步操作,它让异步代码具有同步代码的可读性和简洁性,同时代码结构和逻辑清晰。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
async function showData() {
try {
const data = await fetchData();
console.log(`Name: ${data.name}, Age: ${data.age}`);
} catch (error) {
console.error(error);
}
}
showData();
代码解析:
上述代码中的fetchData函数与Promise方法完全相同,所不同的是,showData函数前面加了async关键字表示该函数里面可能含有await语句,await关键字可以暂停异步操作,等待数据返回后再继续执行。async函数返回一个Promise对象,可以使用try...catch语句捕获异常。
优点:代码简洁易读,结构清晰,具有同步代码的风格,可以避免回调嵌套和Promise链式调用带来的复杂性。
缺点:需要理解Promise的工作原理,同时不能在非async函数中使用await。
四、EventEmitter
EventEmitter是Node.js中基于发布 / 订阅模式实现的异步编程实现方式,通过监听事件的方式处理异步操作。
const EventEmitter = require('events');
function fetchData() {
const emitter = new EventEmitter();
setTimeout(() => {
const data = { name: "John", age: 30 };
emitter.emit('data', data);
}, 1000);
return emitter;
}
const emitter = fetchData();
emitter.on('data', data => console.log(`Name: ${data.name}, Age: ${data.age}`));
代码解析:
上述代码中的EventEmitter模块用于定义事件和事件监听器,fetchData函数将emitter对象作为返回值,使用emit方法传递数据。使用on方法监听'data'事件,当事件触发时,调用事件监听器中的回调函数。
优点:可以处理多个异步事件并发执行,代码结构清晰,灵活便利。
缺点:使用时需要了解事件监听器的工作机制,容易造成内存泄漏,很难呈现流程控制原理。