MST

星途 面试题库

面试题:JavaScript 闭包在复杂异步场景中的应用及优化

假设存在一个复杂的异步操作场景,涉及多个异步任务的顺序执行与并行执行,使用闭包来设计一个解决方案,并说明这样设计的优势以及如何对闭包的使用进行性能优化。
25.9万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

使用闭包的解决方案示例(以JavaScript为例)

function asyncTask1(callback) {
    setTimeout(() => {
        console.log('Async Task 1 completed');
        callback();
    }, 1000);
}

function asyncTask2(callback) {
    setTimeout(() => {
        console.log('Async Task 2 completed');
        callback();
    }, 1500);
}

function asyncTask3(callback) {
    setTimeout(() => {
        console.log('Async Task 3 completed');
        callback();
    }, 2000);
}

function sequentialTasks() {
    asyncTask1(() => {
        asyncTask2(() => {
            asyncTask3(() => {
                console.log('All sequential tasks completed');
            });
        });
    });
}

function parallelTasks() {
    let completedCount = 0;
    const totalTasks = 3;

    function checkCompletion() {
        completedCount++;
        if (completedCount === totalTasks) {
            console.log('All parallel tasks completed');
        }
    }

    asyncTask1(checkCompletion);
    asyncTask2(checkCompletion);
    asyncTask3(checkCompletion);
}

// 执行顺序执行任务
sequentialTasks();
// 执行并行执行任务
parallelTasks();

设计优势

  1. 代码封装与模块化:闭包可以将相关的逻辑和数据封装在一起,使得代码结构更加清晰,易于维护和理解。例如在上述代码中,每个异步任务都有自己独立的函数封装,并且通过闭包将任务之间的依赖关系和执行逻辑组织起来。
  2. 数据隐私与保护:闭包内部可以访问外部函数的变量,同时这些变量对于外部环境是不可见的,提供了一定程度的数据隐私保护。在复杂的异步场景中,可能会有一些中间状态或临时数据,通过闭包可以将它们隐藏起来,避免被外部错误修改。
  3. 灵活的任务控制:通过闭包可以灵活地控制异步任务的执行顺序和条件。在顺序执行的例子中,通过在回调函数中嵌套调用下一个异步任务来实现顺序执行;在并行执行的例子中,通过闭包内的计数器和检查完成的逻辑来控制任务的整体完成情况。

性能优化

  1. 减少闭包嵌套深度:过深的闭包嵌套(如回调地狱)会导致代码可读性变差,并且可能会增加内存消耗。可以使用一些异步控制流库(如async库)或者async/await语法(在支持的语言中,如JavaScript)来扁平化异步操作,减少嵌套。例如在JavaScript中,可以将上述顺序执行的代码改写为:
async function sequentialTasks() {
    await asyncTask1();
    await asyncTask2();
    await asyncTask3();
    console.log('All sequential tasks completed');
}
  1. 及时释放闭包引用:当闭包不再需要时,确保相关的引用被释放,以避免内存泄漏。例如,如果闭包中持有对大型对象的引用,在任务完成后,将这些引用设置为null,让垃圾回收机制可以回收这些内存。
  2. 复用闭包函数:如果有多个地方需要使用相同逻辑的闭包,可以将其提取为一个公共函数,避免重复创建闭包带来的性能开销。