Skip to content
On this page

JavaScript 异步编程

异步编程是JavaScript的核心特性之一,允许程序在等待长时间操作(如网络请求、文件读取)时继续执行其他任务。理解异步编程对于开发现代Web应用至关重要。

异步编程基础

同步 vs 异步

同步代码:代码按顺序执行,每一步都必须等待前一步完成。

javascript
console.log('第一步');
console.log('第二步');
console.log('第三步');
// 输出顺序:第一步 -> 第二步 -> 第三步

异步代码:代码可以启动一个操作后立即继续执行,操作完成后再处理结果。

javascript
console.log('第一步');
setTimeout(() => console.log('异步操作'), 0);
console.log('第二步');
// 输出顺序:第一步 -> 第二步 -> 异步操作

JavaScript 单线程与事件循环

JavaScript运行在单线程上,使用事件循环处理异步操作:

javascript
// 调用栈(Call Stack)、回调队列(Callback Queue)和事件循环(Event Loop)协同工作
console.log('开始');

setTimeout(() => {
  console.log('setTimeout 1');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 1');
});

setTimeout(() => {
  console.log('setTimeout 2');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 2');
});

console.log('结束');

// 输出顺序:
// 开始
// 结束
// Promise 1
// Promise 2
// setTimeout 1
// setTimeout 2

回调函数

回调函数是最早的异步处理方式,但也容易导致"回调地狱":

基本回调

javascript
// 简单的异步操作
function fetchData(callback) {
  setTimeout(() => {
    const data = '模拟数据';
    callback(null, data); // 第一个参数是错误,第二个是数据
  }, 1000);
}

fetchData((error, data) => {
  if (error) {
    console.error('发生错误:', error);
  } else {
    console.log('获取到数据:', data);
  }
});

回调地狱(Callback Hell)

javascript
// 串行执行多个异步操作
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getEvenEvenMoreData(c, function(d) {
        // 嵌套过深,难以维护
        console.log('最终数据:', d);
      });
    });
  });
});

解决回调地狱

javascript
// 使用命名函数
function step1Callback(err, result1) {
  if (err) return console.error(err);
  step2(result1, step2Callback);
}

function step2Callback(err, result2) {
  if (err) return console.error(err);
  step3(result2, step3Callback);
}

function step3Callback(err, result3) {
  if (err) return console.error(err);
  console.log('最终结果:', result3);
}

step1(step1Callback);

Promise

Promise是ES6引入的异步编程解决方案,用于更好地处理异步操作:

Promise 基础

javascript
// Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5; // 模拟随机成功/失败
      if (success) {
        resolve('数据获取成功');
      } else {
        reject(new Error('数据获取失败'));
      }
    }, 1000);
  });
}

// 使用Promise
fetchData()
  .then(data => {
    console.log(data);
    return data.toUpperCase(); // 可以链式调用
  })
  .then(upperData => {
    console.log('处理后的数据:', upperData);
  })
  .catch(error => {
    console.error('错误:', error.message);
  });

Promise 静态方法

javascript
// Promise.resolve() - 创建已解决的Promise
const resolvedPromise = Promise.resolve('已解决的Promise');

// Promise.reject() - 创建已拒绝的Promise
const rejectedPromise = Promise.reject(new Error('已拒绝的Promise'));

// Promise.all() - 所有Promise都成功才成功,任何一个失败则整体失败
const promises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
];

Promise.all(promises)
  .then(results => console.log('所有Promise结果:', results)) // [1, 2, 3]
  .catch(error => console.error('有Promise失败:', error));

// Promise.allSettled() - 等待所有Promise完成,无论成功或失败
Promise.allSettled([
  Promise.resolve('成功'),
  Promise.reject('失败'),
  Promise.resolve('成功')
])
  .then(results => {
    console.log('所有Promise完成:', results);
    // 结果: [{status: 'fulfilled', value: '成功'}, {status: 'rejected', reason: '失败'}, {status: 'fulfilled', value: '成功'}]
  });

// Promise.race() - 第一个完成的Promise决定结果
Promise.race([
  new Promise(resolve => setTimeout(() => resolve(''), 2000)),
  new Promise(resolve => setTimeout(() => resolve(''), 1000)),
  new Promise((_, reject) => setTimeout(() => reject('更快'), 500))
])
  .then(result => console.log('最快的结果:', result)) // '更快'(因为reject更快)
  .catch(error => console.error('最快的错误:', error));

Promise 链式调用

javascript
function fetchUser(id) {
  return new Promise(resolve => {
    setTimeout(() => resolve({ id, name: `用户${id}` }), 500);
  });
}

function fetchUserPosts(userId) {
  return new Promise(resolve => {
    setTimeout(() => resolve([`帖子1-${userId}`, `帖子2-${userId}`]), 500);
  });
}

// 链式调用
fetchUser(123)
  .then(user => {
    console.log('获取用户:', user);
    return fetchUserPosts(user.id); // 返回Promise
  })
  .then(posts => {
    console.log('获取帖子:', posts);
    return posts.length; // 返回普通值
  })
  .then(postCount => {
    console.log('帖子数量:', postCount);
  })
  .catch(error => {
    console.error('操作失败:', error);
  });

async/await

ES2017引入的async/await语法使异步代码看起来像同步代码:

基本用法

javascript
// 使用async关键字标记函数
async function getData() {
  try {
    // 使用await等待Promise解决
    const data = await fetchData();
    console.log('获取到数据:', data);
    return data;
  } catch (error) {
    console.error('错误:', error.message);
    throw error; // 重新抛出错误
  }
}

// 调用async函数
getData().then(result => {
  console.log('函数执行完成,结果:', result);
});

并行执行

javascript
async function fetchMultipleData() {
  try {
    // 并行执行(同时发起请求)
    const [users, posts, comments] = await Promise.all([
      fetch('/api/users'),
      fetch('/api/posts'),
      fetch('/api/comments')
    ]);
    
    const [usersData, postsData, commentsData] = await Promise.all([
      users.json(),
      posts.json(),
      comments.json()
    ]);
    
    return { users: usersData, posts: postsData, comments: commentsData };
  } catch (error) {
    console.error('获取数据失败:', error);
  }
}

// 或者使用Promise.allSettled来处理可能部分失败的情况
async function fetchMultipleDataSafe() {
  const results = await Promise.allSettled([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json())
  ]);
  
  const data = {};
  results.forEach((result, index) => {
    const keys = ['users', 'posts', 'comments'];
    if (result.status === 'fulfilled') {
      data[keys[index]] = result.value;
    } else {
      console.error(`${keys[index]} 获取失败:`, result.reason);
      data[keys[index]] = null;
    }
  });
  
  return data;
}

错误处理

javascript
// 全局错误处理
async function handleRequest(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    if (error instanceof TypeError) {
      console.error('网络错误:', error.message);
    } else if (error.message.includes('HTTP错误')) {
      console.error('服务器错误:', error.message);
    } else {
      console.error('未知错误:', error.message);
    }
    throw error; // 重新抛出,让调用者处理
  }
}

// 通用错误处理函数
async function safeAsync(asyncFn) {
  try {
    const result = await asyncFn();
    return [null, result];
  } catch (error) {
    return [error, null];
  }
}

// 使用示例
async function example() {
  const [error, data] = await safeAsync(() => fetch('/api/data'));
  if (error) {
    console.error('请求失败:', error);
  } else {
    console.log('数据:', data);
  }
}

Fetch API

现代浏览器中的Fetch API基于Promise,用于发起网络请求:

基本用法

javascript
// GET 请求
async function fetchUsers() {
  try {
    const response = await fetch('/api/users');
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const users = await response.json();
    return users;
  } catch (error) {
    console.error('获取用户失败:', error);
    throw error;
  }
}

// POST 请求
async function createUser(userData) {
  try {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(userData)
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const newUser = await response.json();
    return newUser;
  } catch (error) {
    console.error('创建用户失败:', error);
    throw error;
  }
}

请求配置

javascript
// 复杂请求示例
async function apiRequest(url, options = {}) {
  const defaultOptions = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
    credentials: 'include', // 包含cookies
  };
  
  const config = { ...defaultOptions, ...options };
  
  // 添加认证头
  const token = localStorage.getItem('authToken');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  
  try {
    const response = await fetch(url, config);
    
    // 检查响应状态
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.message || `HTTP ${response.status}`);
    }
    
    // 根据内容类型解析响应
    const contentType = response.headers.get('content-type');
    if (contentType && contentType.includes('application/json')) {
      return await response.json();
    } else {
      return await response.text();
    }
  } catch (error) {
    if (error.name === 'TypeError') {
      throw new Error('网络连接失败');
    }
    throw error;
  }
}

生成器与异步编程

Generator函数也可以用于异步编程:

javascript
// 简单的Generator Runner
function run(generator) {
  const iterator = generator();
  
  function handle(result) {
    if (result.done) return Promise.resolve(result.value);
    
    return Promise.resolve(result.value).then(res => {
      return handle(iterator.next(res));
    }).catch(err => {
      return handle(iterator.throw(err));
    });
  }
  
  try {
    return handle(iterator.next());
  } catch (error) {
    return Promise.reject(error);
  }
}

// 使用Generator处理异步
function* asyncOperation() {
  try {
    const user = yield fetch('/api/user').then(r => r.json());
    const posts = yield fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
    return { user, posts };
  } catch (error) {
    console.error('Generator错误:', error);
  }
}

// 执行
run(asyncOperation).then(result => {
  console.log('结果:', result);
});

异步迭代器

ES2018引入了异步迭代器:

javascript
// 异步可迭代对象
class AsyncQueue {
  constructor() {
    this.items = [];
    this.waiting = [];
  }
  
  push(item) {
    if (this.waiting.length > 0) {
      const resolver = this.waiting.shift();
      resolver({ done: false, value: item });
    } else {
      this.items.push(item);
    }
  }
  
  async *[Symbol.asyncIterator]() {
    while (true) {
      if (this.items.length > 0) {
        yield this.items.shift();
      } else {
        await new Promise(resolve => {
          this.waiting.push(resolve);
        });
      }
    }
  }
}

// 使用异步迭代器
async function processQueue() {
  const queue = new AsyncQueue();
  
  // 模拟添加项目
  setTimeout(() => queue.push('项目1'), 1000);
  setTimeout(() => queue.push('项目2'), 2000);
  setTimeout(() => queue.push('项目3'), 3000);
  
  // 异步迭代
  for await (const item of queue) {
    console.log('处理:', item);
  }
}

实际应用示例

API 服务封装

javascript
class ApiService {
  constructor(baseURL, defaultHeaders = {}) {
    this.baseURL = baseURL;
    this.defaultHeaders = {
      'Content-Type': 'application/json',
      ...defaultHeaders
    };
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const config = {
      headers: { ...this.defaultHeaders, ...options.headers },
      ...options
    };
    
    try {
      const response = await fetch(url, config);
      
      if (!response.ok) {
        throw new Error(`请求失败: ${response.status} ${response.statusText}`);
      }
      
      const contentType = response.headers.get('content-type');
      if (contentType && contentType.includes('application/json')) {
        return await response.json();
      }
      
      return await response.text();
    } catch (error) {
      console.error(`API请求错误 [${options.method || 'GET'}] ${url}:`, error);
      throw error;
    }
  }
  
  get(endpoint, params = {}) {
    const queryString = new URLSearchParams(params).toString();
    const url = queryString ? `${endpoint}?${queryString}` : endpoint;
    return this.request(url, { method: 'GET' });
  }
  
  post(endpoint, data) {
    return this.request(endpoint, {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
  
  put(endpoint, data) {
    return this.request(endpoint, {
      method: 'PUT',
      body: JSON.stringify(data)
    });
  }
  
  delete(endpoint) {
    return this.request(endpoint, { method: 'DELETE' });
  }
}

// 使用API服务
const api = new ApiService('https://api.example.com');

async function example() {
  try {
    const users = await api.get('/users');
    const newUser = await api.post('/users', { name: 'John', email: 'john@example.com' });
    console.log('用户列表:', users);
    console.log('新用户:', newUser);
  } catch (error) {
    console.error('API操作失败:', error);
  }
}

并发控制

javascript
// 限制并发数量的异步任务执行器
class ConcurrencyController {
  constructor(limit = 5) {
    this.limit = limit;
    this.running = 0;
    this.queue = [];
  }
  
  async add(asyncFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        asyncFn,
        resolve,
        reject
      });
      this.process();
    });
  }
  
  async process() {
    if (this.running >= this.limit || this.queue.length === 0) {
      return;
    }
    
    this.running++;
    const { asyncFn, resolve, reject } = this.queue.shift();
    
    try {
      const result = await asyncFn();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.running--;
      this.process(); // 继续处理队列
    }
  }
}

// 使用并发控制器
const controller = new ConcurrencyController(3); // 最多同时执行3个任务

async function fetchWithLimit(urls) {
  const promises = urls.map(url => 
    controller.add(() => fetch(url).then(r => r.json()))
  );
  
  return Promise.all(promises);
}

异步编程是JavaScript开发的核心技能,掌握Promise、async/await等现代异步编程模式对于构建高性能的Web应用至关重要。