MST
星途 面试题库

面试题:JavaScript期约(Promise)链式调用的性能优化与复杂场景

在一个复杂的业务场景中,有一系列的Promise操作,这些Promise操作可能会涉及到网络请求、文件读取等I/O操作,且这些操作之间存在依赖关系。请设计一个基于Promise链式调用的解决方案,实现高效执行并处理可能出现的并发控制问题,同时分析如何优化性能以避免阻塞主线程,以及如何处理大量Promise并发时可能出现的内存问题。
29.5万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

基于Promise链式调用的解决方案

  1. 链式调用实现依赖操作:通过将Promise操作依次链式调用,确保前一个操作完成后才执行下一个。例如:
function ioOperation1() {
    return new Promise((resolve, reject) => {
        // 模拟网络请求或文件读取
        setTimeout(() => {
            resolve('Result of operation 1');
        }, 1000);
    });
}

function ioOperation2(result1) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Result of operation 2 with input ${result1}`);
        }, 1000);
    });
}

ioOperation1()
   .then(result1 => ioOperation2(result1))
   .then(finalResult => {
        console.log(finalResult);
    })
   .catch(error => {
        console.error('Error:', error);
    });
  1. 并发控制:使用Promise.allPromise.race进行并发操作控制。例如,如果有多个独立的Promise操作可以并发执行,但要等到所有操作完成,可以使用Promise.all
function ioOperationA() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Result of A');
        }, 1500);
    });
}

function ioOperationB() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Result of B');
        }, 1000);
    });
}

Promise.all([ioOperationA(), ioOperationB()])
   .then(results => {
        console.log(results);
    })
   .catch(error => {
        console.error('Error:', error);
    });

如果只需要第一个完成的Promise结果,可以使用Promise.race

Promise.race([ioOperationA(), ioOperationB()])
   .then(result => {
        console.log(result);
    })
   .catch(error => {
        console.error('Error:', error);
    });

避免阻塞主线程

  1. 使用微任务队列:Promise的回调是放入微任务队列中的,这意味着它们会在当前宏任务结束后,下一个宏任务开始前执行。这可以避免阻塞主线程,让主线程有机会处理其他任务。例如,DOM渲染等操作可以在Promise回调执行前完成。
  2. Web Workers(适用于CPU密集型任务):对于一些CPU密集型的计算,如果在Promise操作中存在,可以使用Web Workers将这些任务放到后台线程执行,避免阻塞主线程。例如:
// main.js
const worker = new Worker('worker.js');
worker.onmessage = function(event) {
    console.log('Result from worker:', event.data);
};
worker.postMessage('Some data to process');

// worker.js
self.onmessage = function(event) {
    // 模拟CPU密集型计算
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
        result += i;
    }
    self.postMessage(result);
};

处理大量Promise并发时的内存问题

  1. 限制并发数量:使用队列和Promise.all的组合来限制同时执行的Promise数量。例如:
function limitConcurrentPromises(promises, limit) {
    return new Promise((resolve, reject) => {
        let completedCount = 0;
        let results = [];
        const executeNext = () => {
            if (promises.length === 0 && completedCount === results.length) {
                resolve(results);
                return;
            }
            const currentPromises = promises.splice(0, limit);
            Promise.all(currentPromises)
               .then(values => {
                    results = results.concat(values);
                    completedCount += values.length;
                    executeNext();
                })
               .catch(error => {
                    reject(error);
                });
        };
        executeNext();
    });
}

// 使用示例
const manyPromises = Array.from({ length: 100 }, (_, i) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(`Promise ${i} completed`);
        }, 100 * i);
    });
});

limitConcurrentPromises(manyPromises, 10)
   .then(results => {
        console.log(results);
    })
   .catch(error => {
        console.error('Error:', error);
    });
  1. 及时释放资源:在Promise操作完成后,及时释放相关资源,如关闭文件句柄、取消网络请求等,以避免内存泄漏。例如,在Node.js中读取文件后及时关闭文件描述符:
const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);
const closeFile = util.promisify(fs.close);

const fd = fs.openSync('test.txt', 'r');
readFile(fd, 'utf8')
   .then(data => {
        console.log(data);
        return closeFile(fd);
    })
   .catch(error => {
        console.error('Error:', error);
    });