魯斯前端布魯斯前端

文章中英模式

布魯斯前端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));