基于Promise+async/await的异步请求调度器设计解析

255 阅读3分钟

异步请求调度器是现代前端开发中处理并发请求、控制请求顺序和优先级的重要工具。下面我将详细介绍如何基于Promise和async/await设计一个高效的异步请求调度器。

一、核心概念与设计原理

  1. Promise基础‌:Promise是异步编程的基础,它表示一个可能现在、将来或永远不可用的值。Promise有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。

  2. async/await优势‌:async/await是建立在Promise之上的语法糖,使异步代码看起来像同步代码,提高可读性和可维护性。async函数隐式返回Promise,await用于等待Promise完成。

  3. 调度器目标‌:

    • 控制并发请求数量
    • 管理请求优先级
    • 处理请求失败重试
    • 提供请求取消功能

二、基础调度器实现

下面是一个基本的并发控制调度器实现:

class RequestScheduler {
  constructor(maxConcurrent = 5) {
    this.maxConcurrent = maxConcurrent;
    this.queue = [];
    this.activeCount = 0;
  }

  add(requestFn, priority = 0) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        requestFn,
        resolve,
        reject,
        priority
      });
      this.queue.sort((a, b) => b.priority - a.priority);
      this.run();
    });
  }

  async run() {
    if (this.activeCount >= this.maxConcurrent || !this.queue.length) return;

    this.activeCount++;
    const { requestFn, resolve, reject } = this.queue.shift();

    try {
      const result = await requestFn();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.activeCount--;
      this.run();
    }
  }
}

三、高级功能扩展

1. 请求重试机制

class RetryScheduler extends RequestScheduler {
  constructor(maxConcurrent = 5, maxRetries = 3) {
    super(maxConcurrent);
    this.maxRetries = maxRetries;
  }

  async run() {
    if (this.activeCount >= this.maxConcurrent || !this.queue.length) return;

    this.activeCount++;
    const { requestFn, resolve, reject, retryCount = 0 } = this.queue.shift();

    try {
      const result = await requestFn();
      resolve(result);
    } catch (error) {
      if (retryCount < this.maxRetries) {
        this.queue.push({
          requestFn,
          resolve,
          reject,
          retryCount: retryCount + 1
        });
        this.run();
      } else {
        reject(error);
      }
    } finally {
      this.activeCount--;
      this.run();
    }
  }
}

2. 请求取消功能

class CancelableScheduler extends RequestScheduler {
  add(requestFn, priority = 0) {
    const controller = new AbortController();
    
    const wrappedRequest = async () => {
      if (controller.signal.aborted) {
        throw new Error('Request aborted');
      }
      return requestFn(controller.signal);
    };

    const promise = super.add(wrappedRequest, priority);
    
    return {
      promise,
      abort: () => controller.abort()
    };
  }
}

四、实际应用场景

1. 图片懒加载批量请求

const scheduler = new RequestScheduler(3); // 最多同时加载3张图片

async function lazyLoadImages(imageUrls) {
  const loadImage = (url) => 
    new Promise((resolve) => {
      const img = new Image();
      img.src = url;
      img.onload = () => resolve(img);
    });

  const promises = imageUrls.map(url => 
    scheduler.add(() => loadImage(url))
  );

  return Promise.all(promises);
}

2. API接口优先级调度

const apiScheduler = new RequestScheduler(2);

// 高优先级请求(用户操作触发)
async function submitFormData(data) {
  return apiScheduler.add(
    () => fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) }),
    10 // 高优先级
  );
}

// 低优先级请求(后台预加载)
async function prefetchData() {
  return apiScheduler.add(
    () => fetch('/api/prefetch'),
    1 // 低优先级
  );
}

3. 文件分片上传

const uploadScheduler = new RetryScheduler(3, 2); // 并发3个,最多重试2次

async function uploadFile(file) {
  const chunkSize = 1024 * 1024; // 1MB
  const chunks = Math.ceil(file.size / chunkSize);
  const uploadPromises = [];

  for (let i = 0; i < chunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);

    uploadPromises.push(
      uploadScheduler.add(() => uploadChunk(chunk, i, file.name))
    );
  }

  await Promise.all(uploadPromises);
  return completeUpload(file.name);
}

async function uploadChunk(chunk, index, filename) {
  const formData = new FormData();
  formData.append('file', chunk);
  formData.append('chunkIndex', index);
  formData.append('filename', filename);

  const response = await fetch('/api/upload-chunk', {
    method: 'POST',
    body: formData
  });

  if (!response.ok) throw new Error('Upload failed');
  return response.json();
}

五、性能优化建议

  1. 动态调整并发数‌:根据网络状况动态调整maxConcurrent值
  2. 请求去重‌:对相同URL或参数的请求进行缓存或合并
  3. 超时处理‌:为每个请求添加超时控制
  4. 错误隔离‌:防止单个请求失败影响整个调度器
  5. 内存管理‌:及时清理已完成请求的引用

六、总结

基于Promise和async/await的异步请求调度器通过以下方式提升应用性能:

  • 有效控制并发请求数量,避免浏览器或服务器过载
  • 通过优先级调度确保关键请求优先处理
  • 简洁的代码结构,易于维护和扩展
  • 统一的错误处理机制,提高应用稳定性