当前位置: 首页 > news >正文

【JavaScript】异步编程

在这里插入图片描述

个人主页:Guiat
归属专栏:HTML CSS JavaScript

在这里插入图片描述

文章目录

  • 1. 异步编程基础
    • 1.1 同步与异步
      • 1.1.1 同步执行
      • 1.1.2 异步执行
    • 1.2 JavaScript 事件循环
  • 2. 回调函数
    • 2.1 基本回调模式
    • 2.2 错误处理
    • 2.3 回调地狱
  • 3. Promise
    • 3.1 Promise 基础
    • 3.2 Promise 链式调用
    • 3.3 Promise 组合
  • 4. Async/Await
    • 4.1 基本语法
    • 4.2 串行与并行执行
    • 4.3 处理循环中的异步操作
  • 5. 异步迭代器与生成器
    • 5.1 异步迭代器
    • 5.2 异步生成器
  • 6. 实际应用场景
    • 6.1 API 请求与错误重试
    • 6.2 异步数据加载与缓存
    • 6.3 异步任务队列
    • 6.4 异步状态管理

正文

1. 异步编程基础

JavaScript 是单线程语言,异步编程是处理非阻塞操作的关键机制。

1.1 同步与异步

1.1.1 同步执行

所有操作按顺序执行,后续任务必须等待前一个任务完成。

console.log("任务 1");
console.log("任务 2");
console.log("任务 3");
// 输出顺序: 任务 1, 任务 2, 任务 3

1.1.2 异步执行

允许某些操作在后台进行,不阻塞主线程执行。

console.log("任务 1");
setTimeout(() => {
  console.log("任务 2"); // 异步任务
}, 1000);
console.log("任务 3");
// 输出顺序: 任务 1, 任务 3, 任务 2

1.2 JavaScript 事件循环

JavaScript 通过事件循环处理异步操作,包含以下关键组件:

  • 调用栈(Call Stack):执行同步代码
  • 任务队列(Task Queue):存放待执行的回调函数
  • 微任务队列(Microtask Queue):优先级高于宏任务队列的队列
  • 事件循环(Event Loop):不断检查调用栈是否为空,并将任务从队列移入栈中
console.log("开始");

setTimeout(() => {
  console.log("宏任务 (setTimeout)");
}, 0);

Promise.resolve().then(() => {
  console.log("微任务 (Promise)");
});

console.log("结束");

// 输出顺序: 开始, 结束, 微任务 (Promise), 宏任务 (setTimeout)

2. 回调函数

回调函数是异步编程的基础,是传递给另一个函数的函数,在特定事件或条件满足时被执行。

2.1 基本回调模式

function fetchData(callback) {
  // 模拟网络请求
  setTimeout(() => {
    const data = { name: "John", age: 30 };
    callback(data);
  }, 1000);
}

// 使用回调获取数据
fetchData(function(data) {
  console.log("获取的数据:", data);
});

console.log("请求已发送,等待数据...");

2.2 错误处理

回调函数通常采用 “错误优先” 的模式进行错误处理。

function fetchData(callback) {
  setTimeout(() => {
    // 模拟随机错误
    const success = Math.random() > 0.3;
    if (success) {
      callback(null, { name: "John", age: 30 });
    } else {
      callback(new Error("无法获取数据"), null);
    }
  }, 1000);
}

// 使用错误优先回调
fetchData(function(error, data) {
  if (error) {
    console.error("发生错误:", error.message);
    return;
  }
  console.log("获取的数据:", data);
});

2.3 回调地狱

嵌套回调会导致代码可读性差,难以维护,俗称"回调地狱"。

// 回调地狱示例
getUser(function(user) {
  getProfile(user.id, function(profile) {
    getFriends(profile.id, function(friends) {
      getPhotos(friends[0].id, function(photos) {
        console.log(photos);
        // 更多嵌套回调...
      }, function(error) {
        console.error("获取照片失败:", error);
      });
    }, function(error) {
      console.error("获取好友失败:", error);
    });
  }, function(error) {
    console.error("获取资料失败:", error);
  });
}, function(error) {
  console.error("获取用户失败:", error);
});

3. Promise

Promise 是异步编程的一种解决方案,用于表示一个异步操作的最终完成(或失败)及其结果值。

3.1 Promise 基础

Promise 有三种状态:

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)
// 创建一个 Promise
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = Math.random() > 0.3;
    if (success) {
      resolve("操作成功");  // 成功时调用
    } else {
      reject(new Error("操作失败"));  // 失败时调用
    }
  }, 1000);
});

// 处理 Promise 结果
promise
  .then(result => {
    console.log(result);  // 成功时执行
    return "处理后的数据";
  })
  .then(data => {
    console.log("链式调用:", data);
  })
  .catch(error => {
    console.error("错误处理:", error.message);  // 失败时执行
  })
  .finally(() => {
    console.log("无论成功或失败都会执行");  // 总是执行
  });

3.2 Promise 链式调用

Promise 可以链式调用,避免回调地狱。

// 使用 Promise 重构回调地狱
function getUser(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({ id: userId, name: "John" }), 1000);
  });
}

function getProfile(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({ id: userId, hobbies: ["reading", "music"] }), 1000);
  });
}

function getFriends(profileId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve([{ id: 101, name: "Alice" }, { id: 102, name: "Bob" }]), 1000);
  });
}

// 链式调用
getUser(1)
  .then(user => {
    console.log("用户:", user);
    return getProfile(user.id);
  })
  .then(profile => {
    console.log("个人资料:", profile);
    return getFriends(profile.id);
  })
  .then(friends => {
    console.log("好友列表:", friends);
  })
  .catch(error => {
    console.error("发生错误:", error);
  });

3.3 Promise 组合

Promise 提供了多种方法来组合处理多个 Promise。

// Promise.all - 等待所有 Promise 完成
Promise.all([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments')
])
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(([users, posts, comments]) => {
    console.log("所有数据:", users, posts, comments);
  })
  .catch(error => {
    console.error("至少一个请求失败:", error);
  });

// Promise.race - 返回最先完成的 Promise 结果
Promise.race([
  new Promise(resolve => setTimeout(() => resolve("快速请求"), 1000)),
  new Promise(resolve => setTimeout(() => resolve("慢速请求"), 3000))
])
  .then(result => {
    console.log("最先完成的是:", result);  // "快速请求"
  });

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

// Promise.any - 返回第一个成功的 Promise (ES2021)
Promise.any([
  new Promise((_, reject) => setTimeout(() => reject(new Error("失败 1")), 1000)),
  new Promise(resolve => setTimeout(() => resolve("成功"), 2000)),
  new Promise((_, reject) => setTimeout(() => reject(new Error("失败 2")), 3000))
])
  .then(result => {
    console.log("第一个成功的结果:", result);  // "成功"
  })
  .catch(error => {
    console.error("所有 Promise 都失败了:", error);
  });

4. Async/Await

Async/Await 是基于 Promise 的语法糖,使异步代码看起来更像同步代码。

4.1 基本语法

// 定义异步函数
async function fetchUserData() {
  try {
    // await 暂停函数执行,直到 Promise 解决
    const response = await fetch('https://api.example.com/users');
    
    // 检查响应状态
    if (!response.ok) {
      throw new Error(`HTTP 错误:${response.status}`);
    }
    
    // 解析 JSON 数据
    const data = await response.json();
    
    console.log("用户数据:", data);
    return data;
  } catch (error) {
    console.error("获取数据失败:", error.message);
    throw error;  // 重新抛出错误
  }
}

// 调用异步函数
fetchUserData()
  .then(data => console.log("处理数据:", data))
  .catch(error => console.error("外部错误处理:", error));

// 或者使用自执行异步函数
(async () => {
  try {
    const data = await fetchUserData();
    console.log("处理数据:", data);
  } catch (error) {
    console.error("外部错误处理:", error);
  }
})();

4.2 串行与并行执行

// 串行执行 - 顺序等待每个请求完成
async function serialFetch() {
  console.time('serial');
  
  const userResponse = await fetch('/api/users');
  const users = await userResponse.json();
  
  const postsResponse = await fetch('/api/posts');
  const posts = await postsResponse.json();
  
  const commentsResponse = await fetch('/api/comments');
  const comments = await commentsResponse.json();
  
  console.timeEnd('serial');
  return { users, posts, comments };
}

// 并行执行 - 同时发起所有请求
async function parallelFetch() {
  console.time('parallel');
  
  // 同时发起所有请求
  const [userResponse, postsResponse, commentsResponse] = await Promise.all([
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
  ]);
  
  // 并行解析 JSON
  const [users, posts, comments] = await Promise.all([
    userResponse.json(),
    postsResponse.json(),
    commentsResponse.json()
  ]);
  
  console.timeEnd('parallel');
  return { users, posts, comments };
}

4.3 处理循环中的异步操作

// 一次处理所有项目 (并行)
async function processItemsParallel(items) {
  const promises = items.map(item => processItem(item));
  const results = await Promise.all(promises);
  return results;
}

// 按顺序处理每一项 (串行)
async function processItemsSequential(items) {
  const results = [];
  for (const item of items) {
    // 等待每一项完成后再处理下一项
    const result = await processItem(item);
    results.push(result);
  }
  return results;
}

// 限制并发数的并行处理
async function processItemsWithConcurrencyLimit(items, limit = 3) {
  const results = [];
  const running = [];
  
  for (const item of items) {
    // 创建处理每项的 Promise
    const promise = processItem(item)
      .then(result => {
        // 从运行列表中移除完成的 Promise
        running.splice(running.indexOf(promise), 1);
        return result;
      });
    
    results.push(promise);
    running.push(promise);
    
    // 如果达到并发限制,等待一个任务完成
    if (running.length >= limit) {
      await Promise.race(running);
    }
  }
  
  // 等待所有任务完成
  return Promise.all(results);
}

5. 异步迭代器与生成器

5.1 异步迭代器

ES2018 引入了异步迭代器,可以使用 for await...of 遍历异步数据源。

// 创建一个异步可迭代对象
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      async next() {
        if (i < 5) {
          // 模拟异步操作
          await new Promise(resolve => setTimeout(resolve, 1000));
          return { value: i++, done: false };
        }
        return { done: true };
      }
    };
  }
};

// 使用 for await...of 遍历
async function iterateAsyncIterable() {
  console.log("开始迭代");
  for await (const value of asyncIterable) {
    console.log("值:", value);
  }
  console.log("迭代完成");
}

iterateAsyncIterable();
// 输出:
// 开始迭代
// 值: 0 (等待1秒后)
// 值: 1 (再等待1秒)
// ...
// 迭代完成 (总共等待5秒)

5.2 异步生成器

异步生成器结合了生成器和异步函数的特性。

// 定义异步生成器函数
async function* createAsyncGenerator() {
  for (let i = 0; i < 5; i++) {
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i;
  }
}

// 使用异步生成器
async function useAsyncGenerator() {
  const asyncGenerator = createAsyncGenerator();
  
  for await (const value of asyncGenerator) {
    console.log("值:", value);
  }
}

useAsyncGenerator();

// 手动迭代异步生成器
async function manualIteration() {
  const asyncGenerator = createAsyncGenerator();
  
  // 首次调用 next()
  let result = await asyncGenerator.next();
  
  while (!result.done) {
    console.log("手动迭代值:", result.value);
    // 获取下一个值
    result = await asyncGenerator.next();
  }
}

6. 实际应用场景

6.1 API 请求与错误重试

async function fetchWithRetry(url, options = {}, retries = 3, backoff = 300) {
  try {
    const response = await fetch(url, options);
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    if (retries <= 0) {
      throw error;
    }
    
    console.warn(`请求失败,等待 ${backoff}ms 后重试...`, error.message);
    
    // 等待指定时间
    await new Promise(resolve => setTimeout(resolve, backoff));
    
    // 递归调用,减少重试次数,增加等待时间
    return fetchWithRetry(url, options, retries - 1, backoff * 2);
  }
}

// 使用
fetchWithRetry('https://api.example.com/data')
  .then(data => console.log('成功获取数据:', data))
  .catch(error => console.error('所有重试都失败:', error));

6.2 异步数据加载与缓存

// 简单的异步缓存系统
class AsyncCache {
  constructor(ttl = 60000) { // 默认缓存时间 60 秒
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  async get(key, fetchFn) {
    const now = Date.now();
    const cached = this.cache.get(key);
    
    // 如果存在有效的缓存项,直接返回
    if (cached && now < cached.expiry) {
      console.log(`Cache hit for key: ${key}`);
      return cached.data;
    }
    
    console.log(`Cache miss for key: ${key}, fetching...`);
    
    try {
      // 获取新数据
      const data = await fetchFn();
      
      // 存入缓存
      this.cache.set(key, {
        data,
        expiry: now + this.ttl
      });
      
      return data;
    } catch (error) {
      // 如果获取失败但存在过期缓存,返回过期数据
      if (cached) {
        console.warn(`Fetch failed, using stale data for: ${key}`);
        return cached.data;
      }
      throw error;
    }
  }
  
  invalidate(key) {
    this.cache.delete(key);
  }
  
  clear() {
    this.cache.clear();
  }
}

// 使用示例
const dataCache = new AsyncCache(5 * 60 * 1000); // 5分钟缓存

async function getUserData(userId) {
  return await dataCache.get(`user:${userId}`, async () => {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) throw new Error('Failed to fetch user');
    return response.json();
  });
}

6.3 异步任务队列

class AsyncTaskQueue {
  constructor(concurrency = 2) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }
  
  async add(task) {
    return new Promise((resolve, reject) => {
      // 将任务添加到队列
      this.queue.push({
        task,
        resolve,
        reject
      });
      
      // 尝试执行下一个任务
      this.runNext();
    });
  }
  
  async runNext() {
    // 如果已达到并发限制或队列为空,则返回
    if (this.running >= this.concurrency || this.queue.length === 0) {
      return;
    }
    
    // 从队列取出一个任务
    const { task, resolve, reject } = this.queue.shift();
    this.running++;
    
    try {
      // 执行任务
      const result = await task();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      // 任务完成,减少计数
      this.running--;
      // 尝试执行下一个任务
      this.runNext();
    }
  }
}

// 使用示例
const queue = new AsyncTaskQueue(3); // 最多3个并发任务

async function processFiles(files) {
  const results = [];
  
  for (const file of files) {
    // 添加异步任务到队列
    const result = await queue.add(async () => {
      console.log(`处理文件: ${file.name}`);
      // 模拟耗时操作
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.random()));
      return { name: file.name, processed: true };
    });
    
    results.push(result);
  }
  
  return results;
}

6.4 异步状态管理

class AsyncState {
  constructor(initialState = {}) {
    this.state = initialState;
    this.listeners = [];
    this.pending = false;
  }
  
  // 获取当前状态
  getState() {
    return this.state;
  }
  
  // 订阅状态变化
  subscribe(listener) {
    this.listeners.push(listener);
    
    // 返回取消订阅函数
    return () => {
      this.listeners = this.listeners.filter(l => l !== listener);
    };
  }
  
  // 异步更新状态
  async update(asyncFn) {
    try {
      this.pending = true;
      this.notifyListeners();
      
      // 调用异步函数,传入当前状态
      const newState = await asyncFn(this.state);
      
      // 更新状态
      this.state = {
        ...this.state,
        ...newState,
      };
      
      this.pending = false;
      this.notifyListeners();
      
      return this.state;
    } catch (error) {
      this.pending = false;
      this.state = {
        ...this.state,
        error,
      };
      
      this.notifyListeners();
      throw error;
    }
  }
  
  // 通知所有监听器
  notifyListeners() {
    for (const listener of this.listeners) {
      listener({
        state: this.state,
        pending: this.pending
      });
    }
  }
}

// 使用示例
const userState = new AsyncState({
  user: null,
  loading: false,
  error: null
});

// 订阅状态变化
const unsubscribe = userState.subscribe(({ state, pending }) => {
  console.log('状态更新:', state, '加载中:', pending);
});

// 异步更新状态
async function fetchUser(id) {
  await userState.update(async (currentState) => {
    try {
      // 开始加载
      const loadingState = { loading: true, error: null };
      
      // 异步操作
      const response = await fetch(`/api/users/${id}`);
      if (!response.ok) throw new Error('Failed to fetch user');
      const user = await response.json();
      
      // 返回新状态
      return { 
        user,
        loading: false
      };
    } catch (error) {
      // 返回错误状态
      return { 
        loading: false, 
        error: error.message 
      };
    }
  });
}

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

相关文章:

  • 图解AUTOSAR_SWS_FunctionInhibitionManager
  • 重新定义PPT创作!ChatPPT发布全球首个AI PPT专用MCP Server
  • 函数作为返回值输出
  • OSI七层模型的封装及解包分用的过程
  • 智能客服系统中的意图识别与分类技术详解
  • 供应链建模大师相关操作笔记——报错可能原因
  • DNS域名解析(以实操为主)
  • 从 macos 切换到 windows 上安装的工具类软件
  • vue3腾讯云直播 前端推流
  • 在nvim的snippet补全片段中增加函数注释的功能
  • React 之 Redux 第三十一节 useDispatch() 和 useSelector()使用以及详细案例
  • windows 10频繁通知A字“出现了问题,无法安装功能。”
  • LangChain入门指南:调用DeepSeek api
  • phpexcel导出下拉框,超过255字符不显示的问题处理
  • 电脑知识 | IPv4数据报分片
  • 【torchserve】农业小模型部署
  • ExternalProject_Add 使用手册与文档详解
  • 前端页面用html2canvas下载为图片
  • 数据治理:让大数据成为真正的“金矿”
  • 从代码学习深度学习 - 序列到序列学习 GRU编解码器 PyTorch 版
  • 门户网站建设意见/发软文是什么意思
  • 做网站属于It行业吗/今天的新闻 最新消息摘抄
  • 淮安哪里有做网站的/互联网营销师国家职业技能标准
  • 成套小说网站模板/怎么学seo基础
  • 怎么用电脑做网站虚拟空间/万网域名购买
  • 网站开发需要解决的问题/武汉做seo公司