鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现执行异步任务

学习如何实现 JavaScript 中的异步任务执行,掌握批量处理、控制并发数、执行顺序管理等技巧,提升前端处理复杂流程的能力。

影片縮圖

懒得看文章?那就来看视频吧

异步任务执行概述

在前端开发中,我们经常需要处理多个异步操作,如 API 请求、文件读取等。有效管理这些异步任务的执行顺序、并发数量和错误处理是一个常见的面试题目。

异步任务执行的常见场景

  • 1. 串行执行:按顺序一个接一个地执行任务
  • 2. 并行执行:同时执行多个任务
  • 3. 并发控制:限制同时执行的任务数量
  • 4. 任务调度:根据优先级或依赖关系安排任务执行顺序
  • 5. 批量处理:将多个任务分批执行

基本串行执行异步任务

串行执行是最简单的异步任务执行方式,确保任务按照指定顺序一个接一个地执行。下面是一个基本实现:

async function executeAsyncTasks(tasks) {
  if (tasks.length === 0) {
    return;
  }
  
  const task = tasks.shift();
  const result = await task();
  console.log(result);
  
  await executeAsyncTasks(tasks);
}

// 使用範例
const tasks = [
  () => Promise.resolve('Task 1 completed'),
  () => Promise.resolve('Task 2 completed'),
  () => Promise.resolve('Task 3 completed')
];

executeAsyncTasks([...tasks]);
// 依次輸出: 
// Task 1 completed
// Task 2 completed
// Task 3 completed

这个实现使用了递归方式一个接一个地执行任务。每次执行一个任务,等待它完成后再执行下一个任务。

基本实现的改进版

async function executeAsyncTasks(tasks) {
  const results = [];
  
  for (const task of tasks) {
    try {
      const result = await task();
      results.push(result);
    } catch (error) {
      results.push({ error });
    }
  }
  
  return results;
}

// 使用範例
const tasks = [
  () => Promise.resolve('Success 1'),
  () => Promise.reject(new Error('Failed')),
  () => Promise.resolve('Success 3')
];

executeAsyncTasks(tasks)
  .then(results => console.log(results))
  .catch(error => console.error(error));
// 輸出: ['Success 1', { error: Error('Failed') }, 'Success 3']

这个改进版本收集了所有任务的结果,并处理了可能的错误,使整个过程更加健壮。

实际应用场景

以下是两个异步任务执行的实际应用示例:

1. 批量上传文件

async function uploadFiles(files, concurrency = 3) {
  const uploadFile = async (file) => {
    // 创建表单数据
    const formData = new FormData();
    formData.append('file', file);
    
    // 模拟上传过程
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData
    });
    
    return await response.json();
  };
  
  // 创建所有上传任务
  const uploadTasks = files.map(file => () => uploadFile(file));
  
  // 使用并发控制上传
  return executeAsyncTasks(uploadTasks);
}

// 使用示例
const fileList = [file1, file2, file3];
uploadFiles(fileList)
  .then(results => console.log('Upload complete:', results))
  .catch(error => console.error('Upload failed:', error));

2. API 数据依赖关系处理

async function fetchUserData(userId) {
  // 步骤 1: 获取用户基本信息
  const userInfo = await fetch(`/api/user/${userId}`)
    .then(res => res.json());
  
  // 步骤 2: 基于用户信息获取相关数据
  const tasks = [
    () => fetch(`/api/posts?userId=${userId}`).then(res => res.json()),
    () => fetch(`/api/followers?userId=${userId}`).then(res => res.json())
  ];
  
  // 依序执行任务
  const [posts, followers] = await executeAsyncTasks(tasks);
  
  // 返回整合后的数据
  return {
    user: userInfo,
    posts,
    followers
  };
}

// 使用示例
fetchUserData('user123')
  .then(data => {
    displayUserProfile(data.user);
    displayUserPosts(data.posts);
    displayFollowers(data.followers);
  })
  .catch(error => console.error('Failed to fetch user data:', error));