MST

星途 面试题库

面试题:Node.js异步编程之回调函数与事件循环

请描述Node.js中回调函数的基本使用方式,并结合事件循环机制,解释在一个包含多个异步I/O操作(如文件读取、网络请求)且都使用回调函数的场景下,程序的执行顺序是怎样的。
43.2万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

回调函数基本使用方式

在Node.js中,回调函数是一种以函数作为参数传递给另一个函数的编程模式。通常在异步操作完成时被调用,以处理操作的结果。

例如,使用fs模块读取文件:

const fs = require('fs');

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

这里fs.readFile是一个异步函数,它接受文件名、编码和一个回调函数作为参数。当文件读取操作完成后,无论成功或失败,都会调用这个回调函数,并传入相应的参数(错误对象err或数据data)。

结合事件循环机制解释多异步I/O操作执行顺序

  1. 事件循环机制简介:Node.js使用事件驱动、非阻塞I/O模型。事件循环负责不断检查调用栈是否为空,当调用栈为空时,事件循环会从任务队列中取出任务并推到调用栈中执行。
  2. 多异步I/O操作场景:假设程序中有多个异步I/O操作,如文件读取和网络请求,且都使用回调函数。
const fs = require('fs');
const http = require('http');

// 文件读取操作
fs.readFile('file1.txt', 'utf8', (err, data1) => {
    if (err) {
        console.error('读取file1.txt错误:', err);
        return;
    }
    console.log('file1.txt内容:', data1);
});

// 网络请求操作
http.get('http://example.com', (res) => {
    let data = '';
    res.on('data', (chunk) => {
        data += chunk;
    });
    res.on('end', () => {
        console.log('网络请求结果:', data);
    });
}).on('error', (err) => {
    console.error('网络请求错误:', err);
});

console.log('主程序继续执行');
  1. 执行顺序
    • 首先,主程序开始执行,同步代码依次执行。fs.readFilehttp.get都是异步操作,它们会将自身的回调函数注册到相应的I/O观察者(文件系统观察者或网络观察者)中,然后立即返回,不会阻塞主程序的执行。
    • 接着,console.log('主程序继续执行');被执行,打印出相应信息。
    • 当文件读取操作完成后,文件系统观察者将文件读取的回调函数放入任务队列。同样,当网络请求完成后,网络观察者将网络请求的回调函数放入任务队列。
    • 事件循环不断检查调用栈,当调用栈为空时,事件循环从任务队列中取出任务(回调函数),按照先进先出的顺序将其推到调用栈中执行。所以,如果文件读取操作先完成,那么文件读取的回调函数会先被执行,打印出文件内容;如果网络请求先完成,那么网络请求的回调函数会先被执行,打印出网络请求结果。

总结来说,异步I/O操作的回调函数在操作完成后进入任务队列,由事件循环按照顺序将其放入调用栈执行,而主程序会在注册完异步操作后继续执行同步代码,不会等待异步操作完成。