ReAct 智能体循环与 JavaScript 异步并发原理深度解析

前言

本文分为两大模块:第一部分讲清 AI Agent 和普通对话大模型的本质区别、ReAct 循环与工具调用核心逻辑;第二部分围绕浏览器异步请求代码,逐行拆解 Promiseasyncawait.then() 的底层运行逻辑与并发特性,完整代码放置在代码解析模块末尾。

一、AI Agent 底层原理:和普通对话 AI 的根本差异

1. 普通对话 LLM:单次单向问答流程

日常使用的普通对话 AI,执行链路是一次性闭环:用户输入指令 → LLM 读取训练静态知识生成文本 → 输出内容后任务直接终止。整个过程不存在自主循环逻辑,也无法主动操作外部资源。举个例子:让普通 AI 写竞品分析报告,它仅依靠训练时沉淀的旧知识生成文字,不会主动联网查询竞品最新动态、抓取财务数据,输出完成流程直接结束,不存在自我校验、补充信息的能力。

2. AI Agent:基于 ReAct 的循环自主执行架构

Agent 最核心的特征是拥有可持续循环的执行链路,标准执行流程命名为 ReAct,由三步动作循环往复运行,直到任务完成或触发终止条件:

  1. Reason(思考) :大模型自主拆解复杂任务,判断当前信息缺口,规划下一步执行动作
  2. Act(行动) :调用外部工具执行操作,包括联网搜索、代码运行、浏览器访问、API 请求等
  3. Observe(观察) :接收工具返回的执行结果,判断现有信息是否足以完成目标任务

一轮循环结束后会自动回到「思考」步骤,持续迭代。

场景示例:Agent 完成竞品分析报告完整循环

  1. 思考:缺少竞品实时业务数据,需要调用搜索工具;行动:发起联网搜索;观察:获取竞品基础动态,识别当前缺失财务报表数据
  2. 再次思考:需要抓取股市财务接口补充数据;行动:请求财务 API;观察:全部所需信息收集完毕,无需继续调用工具
  3. 终止循环,整合所有外部数据输出完整竞品报告

3. Agent 的核心能力:Tool Use 工具调用

工具是 Agent 连接现实世界的唯一载体,没有工具的 Agent 和普通对话 LLM 没有本质区别,仅能输出纯文本内容。可用工具集合直接决定 Agent 的能力边界:

  • 联网搜索工具:获取互联网实时动态、最新资讯
  • 代码执行沙盒:运行代码、批量数据计算、自动化测试
  • 文件读写工具:本地文档生成、内容修改、文件导出
  • 浏览器自动化工具:模拟人工操作网页、抓取页面实时数据
  • 第三方 API 调用:对接企业内部系统、各类业务服务接口

补充说明:ReAct 只是一套通用执行标准,不属于 LangChain 这类开发框架,市面上所有 Agent 开发框架底层都基于这套循环逻辑封装实现。


二、JS 异步代码深度解析(重点 Promise /async/await /.then)

1. 基础概念铺垫:Promise 核心规则

Promise 是 JS 中专门处理异步操作(网络请求、定时器、文件读取)的标准化对象,诞生目的是解决多层嵌套回调形成的「回调地狱」问题。

  1. 三种不可逆状态:

    • pending:初始等待状态,异步操作未执行完成
    • fulfilled:异步执行成功,状态锁定,触发 .then() 回调逻辑
    • rejected:异步执行失败,状态锁定,触发 .catch() 回调逻辑
  2. 状态一旦切换为 fulfilled /rejected,就永久固定,无法再次修改;

  3. 浏览器原生接口 fetch,发起网络请求后返回的值本身就是一个 Promise 对象。

2. async 关键字详解:修饰函数,自动包装返回 Promise

代码中定义了两个请求函数:

javascript

运行

javascript 复制代码
const getStory = async () => fetch('https://v1.hitokoto.cn/?c=i&encode=json')
const getRatp = async () => fetch('https://api.1314.cool/bingimg/?type=json&rand=1')
  • async 仅能用来修饰函数,放置在函数声明、箭头函数的前方;

  • 无论函数内部返回何种类型的值,async 都会自动将返回值包装为 Promise:

    • 若函数返回普通常量 return 123,等价于 Promise.resolve(123)
    • 若函数内部抛出错误,等价于 Promise.reject(错误信息)
  • 示例中 fetch 本身就返回 Promise,外层叠加 async 修饰后,调用 getStory()getRatp() 依旧会得到独立的 Promise 对象,对应两个互不依赖的网络请求。

3. await 关键字:阻塞等待 Promise 完成(注释串行代码对比)

源码中注释了一段串行请求写法:

javascript

运行

ini 复制代码
// const story = await getStory();
// const ratp = await getRatp();
  1. await 存在使用限制:只能写在被 async 修饰的函数内部,单独在全局使用会直接抛出语法错误;
  2. await 核心作用:暂停当前函数内后续代码执行,等待后方 Promise 执行完成并拿到 resolved 结果后,才会继续执行下一行代码;
  3. 这段注释代码属于串行请求:先发起一言接口请求,必须等待接口完整响应结束,才会发起必应壁纸接口请求,总耗时 = 接口 1 耗时 + 接口 2 耗时;
  4. 和下文 Promise.all 并行写法形成明显性能差异,无依赖接口推荐并行执行。

4. Promise.all:并行批量处理多个 Promise(本节核心重点)

javascript

运行

scss 复制代码
Promise.all([getStory(), getRatp()])
  1. 入参要求:接收一个由多个 Promise 实例组成的数组;

  2. 执行逻辑:数组内所有 Promise 同步并发发起,两个网络请求同时发送,总耗时等于耗时最长的接口执行时间,性能远优于串行 await;

  3. 返回规则:

    • 仅当数组内全部 Promise 都执行成功(fulfilled),才会进入后续 .then() 回调;
    • 返回结果为数组,数组内元素顺序和传入 Promise 数组顺序严格对应,与接口响应快慢无关。例如壁纸接口 100ms 返回、一言接口 500ms 返回,最终结果数组依旧保持 [一言结果, 壁纸结果] 的顺序;
  4. 标志性特性「快速失败」:数组中任意一个 Promise 执行失败(404、网络中断、请求报错),整个 Promise.all 会立刻终止执行,直接跳转 .catch(),直接丢弃其余未完成 Promise 的返回结果。

5. .then () 链式回调:接收成功结果,返回新 Promise

javascript

运行

ini 复制代码
.then(response => {
    return Promise.all(response.map(res => res.json()))
})
  1. .then() 的入参,是上一层 Promise.all 成功返回的数组,数组内存储两个 fetch 得到的 Response 响应对象;
  2. res.json() 是 Response 对象自带的异步方法,作用是将接口返回的 JSON 字符串解析为 JS 对象,该方法执行本身依旧返回 Promise;
  3. 通过 response.map 遍历全部 Response 对象,批量调用 .json() 生成一组全新 Promise,再次使用 Promise.all 并行完成所有 JSON 解析操作;
  4. .then 内部若存在 return 语句,返回值会自动包装为 Promise 对象,传递给下一层链式回调,这也是 Promise 链式调用的底层基础。

6. .catch () 统一捕获全链路异步错误

javascript

运行

javascript 复制代码
.catch(response => {
    console.log(response)
})
  1. .catch() 可以捕获整条 Promise 链路中任意位置的失败:包含第一层 Promise.all 接口请求失败、第二层 res.json() 解析时格式报错等所有异常;
  2. 结合 Promise.all 的快速失败特性:只要任意一步异步操作抛出错误,代码会直接跳转到当前 catch,不会执行任何后续 .then 逻辑;
  3. 写法对比:若使用 async/await 串行写法,需要搭配 try/catch 语法才能捕获异常,.then/.catch 链式写法可以集中统一处理全链路异常。

7. async 主函数 main 与程序执行入口

javascript

运行

javascript 复制代码
async function main() {
  Promise.all([getStory(), getRatp()])
    .then(response => {
        return Promise.all(response.map(res => res.json()))
    })
    .catch(response => {
        console.log(response)
    })
}
main();
  • 函数 mainasync 修饰,理论内部可使用 await,本次代码完全依靠 Promise 静态方法实现并发逻辑,未使用 await;
  • 末尾手动调用 main(),页面加载完成后自动触发整套异步请求逻辑,一次性发起两个网络接口调用。

8. 串行 await 和 Promise.all 并行核心区别总结

  1. 串行(注释 await 代码):接口先后顺序执行,总耗时叠加,适用于接口之间存在数据依赖(第二个接口需要第一个接口返回参数);
  2. 并行(Promise.all):同步发起全部无依赖接口,总耗时取最长接口耗时,适用于多独立接口批量请求,大幅优化页面加载速度;
  3. 风险提示:Promise.all 存在快速失败特性,若业务场景需要等待所有请求走完、无论成功失败都保留全部结果,可替换为 Promise.allSettled

完整可运行代码(放置本模块末尾)

html

预览

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<script>
// const streetCarsPromise = fetch('http://data.ratp.fr/api/datasets/1.0/search/?q=paris');
// console.log(streetCarsPromise);
const getStory = async () => 
    fetch('https://v1.hitokoto.cn/?c=i&encode=json')
const getRatp = async () => 
    fetch('https://api.1314.cool/bingimg/?type=json&rand=1')
async function main() {
    // 不存在依赖关系 , 并行以下 Promise 一系列静态方法
    // const story = await getStory();
    // const ratp = await getRatp();
    // 并发的效果 
    // 参数是promise 数组
    // 不管谁先解决,谁后, resolve 的结果会按promise 数组
    // 顺序来收集。最后一个promise resolved 之后, 停下来

   Promise.all([getStory(), getRatp()])
    .then(response => {
        return Promise.all(response.map(res => res.json()))
    })
    .catch(response => {
        console.log(response)
    })
   
}
main();
</script>
</body>
</html>

三、全文总结

  1. AI Agent 的核心竞争力不在于对话能力,而是 ReAct 循环自主思考 + 工具调用;普通 LLM 仅能单次应答,无法自主迭代完成复杂任务,可用工具集合直接决定 Agent 的能力上限。
  2. async 用于修饰函数,让函数天然返回 Promise;await 用于阻塞等待异步结果,多用于存在数据依赖的串行异步逻辑。
  3. Promise.all 实现无依赖异步任务并发,搭配多层 .then() 链式处理分层异步逻辑,.catch() 统一捕获整条链路的所有异常。
  4. fetch 会返回原生 Promise 对象,接口响应后的 .json() 解析操作依旧属于异步 Promise,批量解析多接口 JSON 需要二次使用 Promise.all 并行处理。
相关推荐
北辰alk12 小时前
Next.js 全栈开发指南:从项目初始化到生产环境部署
react.js
胡哈18 小时前
React Compiler:让开发者忘掉 memo 这件事
前端·react.js
光影少年21 小时前
react navite JSBridge 通信机制、异步通信特点
react native·react.js·掘金·金石计划
ndsc_d21 小时前
10分钟搭建B端SaaS数据看板:Paico+TDesign+React实战复盘
前端·人工智能·react.js·ui·前端框架·aigc·tdesign
我是大卫1 天前
React源码解析-第三部分:协调与提交
前端·react.js·源码
甜味弥漫1 天前
React 从类组件到 Hooks:为什么 Hooks 改变了 React?
前端·javascript·react.js
asdzx671 天前
在 React 中轻松实现 PDF 转 Word:纯前端 WASM 方案实战
前端·react.js·pdf
我是大卫2 天前
React源码解析-第一部分:宏观架构
前端·react.js·源码
我是大卫2 天前
React源码解析-第二部分:更新与调度
前端·react.js·源码