JavaScript 异步编程模式

中等 🟡Js/Ts
4 个标签
预计阅读时间:19 分钟
JavaScript异步Promiseasync/await

JavaScript 异步编程模式

JavaScript 是一门单线程语言,异步编程是其核心特性之一,掌握各种异步编程模式对于编写高性能的应用至关重要。

回调函数

回调函数的概念:

作为参数传递给其他函数的函数
在异步操作完成后被调用

回调地狱:

嵌套的回调函数
代码难以阅读和维护
错误处理复杂

代码示例

回调函数示例

javascriptCode
// 基本回调函数
function fetchData(callback) {
  setTimeout(() => {
    const data = { name: 'Alice', age: 25 };
    callback(null, data);
  }, 1000);
}

fetchData((error, data) => {
  if (error) {
    console.error('Error:', error);
    return;
  }
  console.log('Data:', data);
});

// 回调地狱
fetchData((error, data) => {
  if (error) {
    console.error('Error:', error);
    return;
  }
  
  processData(data, (error, processedData) => {
    if (error) {
      console.error('Error:', error);
      return;
    }
    
    saveData(processedData, (error, result) => {
      if (error) {
        console.error('Error:', error);
        return;
      }
      
      console.log('Result:', result);
    });
  });
});

Promise

Promise 的概念:

表示一个异步操作的最终完成或失败
有三种状态:pending、fulfilled、rejected
状态一旦改变就不会再变

Promise 的方法:

then():处理成功的结果
catch():处理错误
finally():无论成功或失败都会执行
all():等待所有 Promise 完成
race():等待第一个 Promise 完成
allSettled():等待所有 Promise 完成或失败
any():等待第一个成功的 Promise

代码示例

Promise 基础示例

javascriptCode
// 创建 Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = Math.random() > 0.5;
    
    if (success) {
      resolve({ name: 'Alice', age: 25 });
    } else {
      reject(new Error('Failed to fetch data'));
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(data => {
    console.log('Data:', data);
    return data;
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Operation completed');
  });

Promise 链式调用

javascriptCode
function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ name: 'Alice', age: 25 });
    }, 1000);
  });
}

function processData(data) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ ...data, processed: true });
    }, 500);
  });
}

function saveData(data) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ success: true, data });
    }, 300);
  });
}

// 链式调用
fetchData()
  .then(data => {
    console.log('Fetched:', data);
    return processData(data);
  })
  .then(data => {
    console.log('Processed:', data);
    return saveData(data);
  })
  .then(result => {
    console.log('Saved:', result);
  })
  .catch(error => {
    console.error('Error:', error);
  });

Promise.all 并发执行

javascriptCode
function fetchUser(id) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id, name: `User ${id}` });
    }, 1000);
  });
}

function fetchPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, userId, title: 'Post 1' },
        { id: 2, userId, title: 'Post 2' }
      ]);
    }, 800);
  });
}

function fetchComments(postId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, postId, text: 'Comment 1' },
        { id: 2, postId, text: 'Comment 2' }
      ]);
    }, 600);
  });
}

// 并发执行
Promise.all([
  fetchUser(1),
  fetchPosts(1),
  fetchComments(1)
])
  .then(([user, posts, comments]) => {
    console.log('User:', user);
    console.log('Posts:', posts);
    console.log('Comments:', comments);
  })
  .catch(error => {
    console.error('Error:', error);
  });

Promise.race 超时处理

javascriptCode
function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ data: 'Some data' });
    }, 2000);
  });
}

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error('Timeout'));
    }, ms);
  });
}

// 使用 Promise.race 实现超时
Promise.race([
  fetchData(),
  timeout(1000)
])
  .then(data => {
    console.log('Data:', data);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

Promise.allSettled 等待所有完成

javascriptCode
const promises = [
  Promise.resolve(1),
  Promise.reject(new Error('Failed')),
  Promise.resolve(3)
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index}: ${result.value}`);
      } else {
        console.log(`Promise ${index}: ${result.reason.message}`);
      }
    });
  });

async/await

async/await 的概念:

基于 Promise 的语法糖
使异步代码看起来像同步代码
更易于阅读和维护

使用方法:

async 函数返回一个 Promise
await 关键字只能在 async 函数中使用
await 会暂停函数执行,直到 Promise 解决

代码示例

async/await 基础示例

javascriptCode
async function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ name: 'Alice', age: 25 });
    }, 1000);
  });
}

async function main() {
  try {
    const data = await fetchData();
    console.log('Data:', data);
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

async/await 错误处理

javascriptCode
async function fetchUser(id) {
  const response = await fetch(`/api/users/${id}`);
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return response.json();
}

async function main() {
  try {
    const user = await fetchUser(1);
    console.log('User:', user);
  } catch (error) {
    console.error('Failed to fetch user:', error.message);
  }
}

main();

async/await 并发执行

javascriptCode
async function fetchUser(id) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id, name: `User ${id}` });
    }, 1000);
  });
}

async function fetchPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, userId, title: 'Post 1' },
        { id: 2, userId, title: 'Post 2' }
      ]);
    }, 800);
  });
}

async function fetchComments(postId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, postId, text: 'Comment 1' },
        { id: 2, postId, text: 'Comment 2' }
      ]);
    }, 600);
  });
}

// 并发执行
async function main() {
  try {
    const [user, posts, comments] = await Promise.all([
      fetchUser(1),
      fetchPosts(1),
      fetchComments(1)
    ]);
    
    console.log('User:', user);
    console.log('Posts:', posts);
    console.log('Comments:', comments);
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

async/await 顺序执行

javascriptCode
async function fetchUser(id) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id, name: `User ${id}` });
    }, 1000);
  });
}

async function fetchPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, userId, title: 'Post 1' }
      ]);
    }, 800);
  });
}

// 顺序执行
async function main() {
  try {
    const user = await fetchUser(1);
    console.log('User:', user);
    
    const posts = await fetchPosts(user.id);
    console.log('Posts:', posts);
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

异步模式最佳实践

错误处理:

使用 try/catch 处理 async/await 错误
使用 catch() 处理 Promise 错误
统一错误处理策略

并发控制:

使用 Promise.all() 并行处理多个异步操作
使用 Promise.race() 处理超时
合理控制并发数量

性能优化:

避免不必要的异步操作
使用缓存减少重复请求
合理使用防抖和节流

代码示例

并发控制

javascriptCode
async function fetchUrl(url) {
  const response = await fetch(url);
  return response.json();
}

async function fetchAll(urls, concurrency = 3) {
  const results = [];
  const executing = [];
  
  for (const url of urls) {
    const promise = fetchUrl(url).then(result => {
      executing.splice(executing.indexOf(promise), 1);
      return result;
    });
    
    executing.push(promise);
    results.push(promise);
    
    if (executing.length >= concurrency) {
      await Promise.race(executing);
    }
  }
  
  return Promise.all(results);
}

const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  'https://api.example.com/data3',
  'https://api.example.com/data4',
  'https://api.example.com/data5'
];

fetchAll(urls, 3)
  .then(results => console.log('Results:', results))
  .catch(error => console.error('Error:', error));

请求重试

javascriptCode
async function fetchWithRetry(url, options = {}, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      return response.json();
    } catch (error) {
      if (i === retries - 1) {
        throw error;
      }
      
      // 指数退避
      const delay = Math.pow(2, i) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

fetchWithRetry('https://api.example.com/data')
  .then(data => console.log('Data:', data))
  .catch(error => console.error('Error:', error));

常见异步场景

网络请求:

使用 fetch 或 axios 发起请求
处理响应和错误
实现请求重试和超时

文件操作:

读取和写入文件
处理大文件
监控文件变化

定时器:

setTimeout 和 setInterval
清除定时器避免内存泄漏
使用 requestAnimationFrame 进行动画

事件处理:

处理 DOM 事件
事件委托
异步事件处理

最佳实践

理解 JavaScript 单线程和异步编程
优先使用 async/await 而非回调函数
合理使用 Promise 方法处理并发
统一错误处理策略
避免回调地狱
控制并发数量
实现请求重试和超时