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

时间:2026-07-09 08:56:53 来源:互联网

本文系统解析两大核心主题:AI Agent与普通对话大模型在ReAct循环上的本质差异,以及JavaScript异步编程中Promise、async、await、.then的底层运行逻辑与并发特性。

前言

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

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

日常使用的普通对话大模型执行链路为一次性闭环:用户输入指令后,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的能力边界:

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

需要补充的是,ReAct仅是一套通用执行标准,并非LangChain这类开发框架,当前所有Agent开发框架底层均基于此循环逻辑封装实现。

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

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

Promise是JavaScript中专门处理异步操作(如网络请求、定时器、文件读取)的标准化对象,其设计初衷在于解决多层嵌套回调形成的“回调地狱”问题。

  1. 三种不可逆状态:
    1. pending:初始等待状态,异步操作尚未完成。
    2. fulfilled:异步执行成功,状态锁定,触发.then()回调逻辑。
    3. rejected:异步执行失败,状态锁定,触发.catch()回调逻辑。
  2. 状态一旦切换为fulfilled或rejected,便永久固定,无法再次修改。
  3. 浏览器原生接口fetch,发起网络请求后返回的值本身即一个Promise对象。

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

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

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')

  1. async仅用于修饰函数,放置在函数声明或箭头函数前方。
  2. 无论函数内部返回何种类型值,async都会自动将返回值包装为Promise:
    1. 若函数返回普通常量如return 123,等价于Promise.resolve(123)
    2. 若函数内部抛出错误,等价于Promise.reject(错误信息)
  3. 示例中fetch本身就返回Promise,外层叠加async修饰后,调用getStory()getRatp()依然得到独立Promise对象,对应两个互不依赖的网络请求。

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

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

javascript

运行

// 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

运行

Promise.all([getStory(), getRatp()])

  1. 入参要求:接收由多个Promise实例组成的数组。
  2. 执行逻辑:数组内所有Promise同步并发发起,两个网络请求同时发送,总耗时等于耗时最长的接口执行时间,性能远优于串行await
  3. 返回规则:
    1. 仅当数组内全部Promise都执行成功(fulfilled),才进入后续.then()回调。
    2. 返回结果为数组,元素顺序与传入Promise数组顺序严格对应,与接口响应快慢无关。例如壁纸接口100ms返回、一言接口500ms返回,最终结果数组依然保持[一言结果, 壁纸结果]的顺序。
  4. 标志性特性“快速失败”:数组中任意一个Promise执行失败(如404、网络中断、请求报错),整个Promise.all会立即终止,直接跳转.catch(),丢弃其余未完成Promise的返回结果。

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

javascript

运行

.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

运行

.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

运行

async function main() {Promise.all([getStory(), getRatp()]).then(response => {return Promise.all(response.map(res => res.json()))}).catch(response => {console.log(response)})}main();

  1. 函数mainasync修饰,理论内部可使用await,但本次代码完全依赖Promise静态方法实现并发,未使用await
  2. 末尾手动调用main(),页面加载后自动触发整套异步请求逻辑,一次性发起两个网络接口调用。

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

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

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

html

预览

html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Documenttitle>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()解析仍属异步操作,批量解析多接口JSON需二次使用Promise.all并行处理。