Skip to content

Promise 与案例 ​

本页对应 1-8Promise及案例,共 4 段视频,约 38 分钟。

学习目标 ​

  • 能用自己的话说明 Promise 是浏览器内置对象,并描述其用于管理异步操作、获取成功或失败结果的作用。
  • 能按“实例化 Promise → 在回调中执行异步操作并调用 resolve/reject → 用 then/catch 接收结果”的步骤独立写出可运行代码。
  • 能准确说出 Promise 的 pending、fulfilled、rejected 三种状态及其触发条件,并验证状态改变后不可再次改变。
  • 能使用 Promise 封装 XHR 请求省份列表,在 then 中接收并渲染数据到页面。
  • 能通过 XHR 的 status 判断 2xx 为成功、非 2xx 为失败,并使用 reject(new Error(...)) 传递异常信息。
  • 能在 catch 中接收异常对象,通过 console.dir 查看 message 属性并将错误信息渲染到页面。

知识点 ​

Promise 是什么:把异步结果装进对象 ​

为什么需要: 定时器、XHR 请求这类异步操作,结果不会立刻返回。如果只靠回调,成功和失败的处理容易散落在各处。Promise 用来表示一个异步操作的最终完成(或失败)及其结果值,本质是“管理异步操作的对象”。

怎么想: 先别把 Promise 想复杂。它就是一个对象:内部执行异步任务,任务结束后用 resolve 表示成功、用 reject 表示失败;外部用 .then 接成功结果,用 .catch 接失败结果。转写稿里反复强调的 resolve、reject 都是函数参数,不是普通变量。

怎么做: 先实例化:

js
// 1. 实例化Promise对象
const p = new Promise((resolve,reject)=>{

})

易错点: new Promise 传入的回调函数会立即执行,所以异步逻辑要写在回调里面;resolve、reject 是函数,调用时才能把结果传出去;Promise 不是“自动等待”的语法,它只是管理异步状态和结果。

为什么学 Promise:逻辑清晰,也能看懂 axios ​

为什么需要: Promise 让成功或失败关联到后续处理函数,逻辑更清晰;同时,axios 内部就是基于 Promise 封装而成的,学会 Promise 才能更好理解 axios 的 .then、.catch 为什么这样用。笔记还提到,Promise 能解决回调函数地狱问题,后面会展开,今天先掌握基础使用。

怎么想: 转写稿里说,axios 通过 .then 拿成功结果、.catch 拿失败结果,这和 Promise 的用法一致;而“小黄人”指的是 XMLHttpRequest,是底层请求对象。先学会 Promise 管异步任务,再看 axios 就不会只停留在“会调用”。

Promise 在 axios 中的位置

怎么做: 把 Promise 的三步记牢:实例化、执行异步并传递结果、获取结果。后面写 axios 时,重点观察它的返回值为什么能 .then、.catch。

易错点: 不要把 .then 当成“一定会成功”,也不要把 .catch 只理解成“代码报错”;这里的失败是 Promise 被 reject 后的失败结果。

Promise 的基本使用三步 ​

为什么需要: 异步任务需要固定套路:谁执行、结果怎么传、外面怎么接。Promise 把这三件事拆清楚。

怎么想: 按转写稿的推导:第一步 new Promise,回调里接收 resolve、reject;第二步在回调里执行异步操作,成功调 resolve(成功结果),失败调 reject(失败结果);第三步用 p.then 接成功,用 p.catch 接失败。

怎么做: 笔记中的核心语法如下:

js
// 1. 实例化Promise对象
const p = new Promise((resolve,reject)=>{
  // 2. 执行异步操作,并传递结果
  // 成功resolve(成功结果) then()执行
  // 失败reject(失败结果) catch()执行
})
// 3. 接收结果
p.then(res=>{
  // 成功
}).catch(err=>{
  // 失败
})

Promise 管理异步任务的语法

按转写稿的调试过程,可以先用定时器模拟异步:

js
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功了')
  }, 1000)
})

p.then(res => {
  console.log(res) // 成功了
}).catch(err => {
  console.log(err)
})

把 resolve('成功了') 换成 reject('失败了'),成功回调不执行,.catch 里会收到“失败了”。

易错点: 一次异步任务通常只调用 resolve 或 reject 中的一个;结果要通过参数传出去,不能只在回调里 console.log;.then 和 .catch 要写在 Promise 对象外面接收结果。

调试时看状态:先 resolve 后 reject 为什么不生效 ​

为什么需要: 知道状态变化,才能理解 .then、.catch 什么时候触发。转写稿中通过控制台查看 PromiseState:实例化后默认是 pending(待定);调用 resolve 后变成 fulfilled(已兑现),触发 .then;调用 reject 后变成 rejected(已拒绝),触发 .catch。

怎么想: resolve、reject 的本质是改变 Promise 状态,状态改变后才会执行对应处理函数。

怎么做: 在浏览器控制台输入 Promise 变量,展开查看 PromiseState。转写稿还测试了:先 reject 再 resolve,最终仍是 rejected,后面的 resolve 不会再把结果传出去。

易错点: Promise 状态一旦从 pending 变成 fulfilled 或 rejected,就不能再改变;不要在一个 Promise 里既想成功又想失败都生效。

小结检查 ​

  1. Promise 是什么?表示(管理)一个异步操作最终状态和结果值的对象。
  2. 为什么学?成功和失败状态可以关联对应处理函数,也能了解 axios 内部运作原理。
  3. 使用步骤?new Promise 执行异步任务;用 resolve 关联 .then 传递成功结果;用 reject 关联 .catch 传递失败结果。

课件图示 ​

课件第15页:定义: Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。

本节目标与为什么要认识 Promise 的状态 ​

这一节的目标是:认识 Promise 的三种状态,并知道状态改变后如何关联成功/失败的处理函数。

为什么要了解状态?因为 Promise 不是“凭空调用 then 和 catch”的。上一节我们写 resolve()、reject() 时,本质上是在改变 Promise 对象的状态;状态一变,才会触发对应的处理函数。搞清楚状态,才能理解 then 和 catch 什么时候执行、代码按什么顺序走。

可以这样想:把 Promise 看成一个“任务状态机”。任务开始时是待定;任务成功后变成已兑现;任务失败后变成已拒绝。then 关联成功,catch 关联失败。怎么做?先记住三种状态,再用代码观察状态变化。

三种状态:pending、fulfilled、rejected ​

每个 Promise 对象必定处于以下三种状态之一:

  1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  2. 已兑现(fulfilled):操作成功完成。
  3. 已拒绝(rejected):操作失败。

这里的英文字符串可以理解为 Promise 对象内部的字符串标识符,用来判断什么时候调用哪一个处理函数。注意 PPT 里出现的 fullfilled 是拼写误差,标准写法是 fulfilled。

怎么想?实例化 new Promise() 后,默认就是 pending;调用 resolve() 后变成 fulfilled;调用 reject() 后变成 rejected。

怎么做?可以在浏览器控制台输入 Promise 变量名,展开后查看 PromiseState。转写稿的调试过程是:先实例化但不写异步结果,控制台查看是 pending;调用 resolve 后查看变成 fulfilled;调用 reject 后查看变成 rejected。

易错点:状态不是结果值。resolve('成功了') 中,fulfilled 是状态,'成功了' 是传递出去的结果值,后面会进入 then 回调的参数。

状态如何关联 then 和 catch ​

Promise 状态变化与处理函数关联示意图

这张图要表达的是:Promise 状态改变后,会关联到不同的处理函数。

  • pending:待定,初始状态,既没成功也没失败。
  • 调用 resolve():状态从 pending 变为 fulfilled,触发 .then(回调函数)。
  • 调用 reject():状态从 pending 变为 rejected,触发 .catch(回调函数)。

为什么需要这样设计?因为异步操作的结果只有两类:成功或失败。Promise 用状态来区分,外部就用 then 和 catch 分别接收。

怎么想?可以记对应关系:resolve → then,reject → catch。怎么做?在 Promise 回调里调用 resolve 或 reject,外部用 then 或 catch 接收结果。

易错点:不要写成 p.then 处理失败、p.catch 处理成功。转写稿反复强调:成功走 then,失败走 catch。

用代码观察状态变化 ​

先看成功情况:

js
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功了')
  }, 1000)
})

p.then(res => {
  console.log(res) // 成功了
})

执行过程:实例化时,Promise 回调立刻执行,里面写了定时器;1 秒后调用 resolve('成功了'),状态从 pending 变成 fulfilled,于是 then 的回调执行,res 接收到 '成功了'。在控制台查看 p,展开 PromiseState 可看到 fulfilled。

失败情况:

js
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('失败了')
  }, 1000)
})

p.catch(err => {
  console.log(err) // 失败了
})

1 秒后调用 reject('失败了'),状态从 pending 变成 rejected,触发 catch,err 接收到失败结果。控制台查看状态为 rejected。

易错点:then/catch 的回调不是实例化时立刻执行,而是等状态改变后触发;Promise 回调本身是实例化时立刻执行的。

状态敲定后不可逆:调试与易错点 ​

笔记特别提醒:每个 Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变。

转写稿的验证过程是:先调用 reject,状态变成 rejected;后面再调用 resolve,发现 then 没有输出,控制台查看状态仍然是 rejected。反过来先 resolve 再 reject,状态也会固定在 fulfilled。

js
const p = new Promise((resolve, reject) => {
  resolve('第一次成功')
  reject('再失败')
})

p.then(res => {
  console.log(res) // 第一次成功
})

p.catch(err => {
  console.log(err) // 不会执行
})

怎么想?状态只能从 pending 转向 fulfilled 或 rejected 一次,之后不可逆。怎么做?调试时在控制台输入变量名,展开看 PromiseState,不要只看有没有打印。易错点:不要以为后写的 reject 会覆盖前面的 resolve;也不要在 pending 阶段就期待 then/catch 执行。

小结与自测 ​

Promise 对象有 3 种状态:待定 pending、已兑现 fulfilled、已拒绝 rejected。状态的作用是:状态改变后,决定关联执行哪个处理函数。成功 resolve 对应 then,失败 reject 对应 catch,且状态一旦敲定不可再改。

保留本节笔记中的小结代码:

html
<details>
  <summary>答案</summary>
  <ul>
    <li>待定 pending,已兑现 fulfilled,已拒绝 rejected</li>
  </ul>
</details>

<details>
  <summary>答案</summary>
  <ul>
    <li>状态改变后,如何关联处理函数</li>
  </ul>
</details>

课件图示 ​

课件第17页:定义: 一个Promise必然处于以下几种状态之一(3种) 1. 待定(pend

本节目标与整体思路 ​

转写稿里的“小黄人”指 XHR/XMLHttpRequest,“Henice”指 then,“cash”指 catch,本文统一按正确术语书写。本节目标是:用 Promise 管理 XHR 异步任务,请求省份列表并展示到页面;再用错误地址演示 404,补上异常处理。需求来自笔记:核心功能是“Promise + XHR 获取省份数据并渲染”,异常处理是“请求失败时把服务器提示信息渲染到页面”。整体结构就是 PPT 中的三步:请求数据 → 传递数据 → 接收并使用数据。

为什么需要: XHR 本身是回调式异步代码,成功、失败、后续渲染容易散落在事件回调里;Promise 可以把“异步操作”和“结果处理”分开,成功走 then,失败走 catch,结构更清楚。
怎么想: 把 XHR 的异步代码放进 new Promise((resolve, reject) => { ... }) 的执行器里;XHR 成功时调用 resolve(数据),失败时调用 reject(错误);外部用 p.then(...).catch(...) 接收。
怎么做: 先写 Promise 外壳,再在内部创建 XHR、open、监听 loadend、send,最后在 then 中渲染。
易错点: 不要忘记 send();不要在 XHR 还没回来时提前 resolve;Promise 状态从 pending 变成 fulfilled 或 rejected 后不可逆,调用 resolve 后再调用 reject 不会再次改变状态。

核心功能:Promise + XHR 获取省份列表 ​

为什么需要: 省份列表来自服务器,必须发 AJAX 请求;用 Promise 管理后,后续渲染逻辑可以集中写在 then 中。
怎么想: XHR 的 loadend 在响应回来后执行,此时能拿到 xhr.response;接口返回的是 JSON 字符串,先 JSON.parse 转成 JS 对象,再 resolve 出去;外部 then 里拿到对象,取 res.list 渲染到 .box。
怎么做: 代码可写成下面的结构。注意接口地址用占位,不编造具体域名;演示异常时再把地址改错,例如在末尾加 123。

js
const p = new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '省份列表接口地址');

  xhr.addEventListener('loadend', () => {
    // 先判断响应状态码,成功范围是 200~299
    if (xhr.status >= 200 && xhr.status < 300) {
      // xhr.response 是 JSON 字符串,转成 JS 对象后传递
      resolve(JSON.parse(xhr.response));
    } else {
      // 失败时把服务器返回的信息包装成错误对象
      reject(new Error(xhr.response));
    }
  });

  xhr.send();
});

p.then(res => {
  // 核心功能:把省份列表渲染到页面
  document.querySelector('.box').innerText = res.list;
}).catch(err => {
  // 异常处理:把错误信息渲染到页面
  document.querySelector('.box').innerHTML = err.message;
});

调试时可以按转写稿的过程理解:先在 loadend 里打印 xhr.status,看到 200,说明请求成功;再打印 xhr.response,确认它是 JSON 字符串;JSON.parse 后在 then 中打印 res,点开后找到 list 属性;最后用 document.querySelector('.box') 拿到盒子,把 res.list 放进去。核心功能中渲染列表用 innerText,因为这里只是展示数据,不解析标签。
易错点: xhr.status 才是响应状态码,不要写成 xhr.state;JSON.parse 只应在成功分支里做;失败时 xhr.response 可能是错误提示文本甚至 HTML 片段,不要盲目 parse;忘记 xhr.send() 会导致请求根本没发出。

异常处理:用状态码判断成功或失败 ​

为什么需要: 只写 then 只能处理成功;请求地址错误、服务器返回 404 时,必须让 Promise 进入 rejected 状态,外部 catch 才能拿到异常并渲染提示。
怎么想: AJAX 判断成功不是看“有没有报错”,而是看响应状态码:xhr.status >= 200 && xhr.status < 300 为成功;不在这个范围就是失败。失败时调用 reject,并把服务器返回的信息用 new Error(...) 包装,外部 catch 的参数 err 就能通过 err.message 取到提示。
怎么做: 在 loadend 中写 if/else;成功 resolve(JSON.parse(xhr.response)),失败 reject(new Error(xhr.response));外部 .catch(err => { document.querySelector('.box').innerHTML = err.message; })。异常信息来自服务器,可能是带标签的提示,所以这里用 innerHTML 渲染。调试时把地址改错,看到状态码从 200 变成 404;打印 xhr.response 能看到服务器返回的“找不到”提示;如果直接 console.log(err) 看不到内部属性,可用 console.dir(err) 查看对象,找到 message 属性后再渲染。
易错点: 不要用 xhr.status === 200 写死,因为成功范围是 200 到 299;404 是“找不到”,不是成功;失败分支不要继续 JSON.parse;err 是错误对象,页面要取 err.message,不是直接把整个对象塞进去。

小结:AJAX 如何判断请求响应成功 ​

本节小结的答案是:响应状态码在大于等于 200 并且小于 300 的范围是成功的。对应到 Promise:成功调用 resolve,状态变为 fulfilled,触发 then;失败调用 reject,状态变为 rejected,触发 catch。实例化后的默认状态是 pending,状态一旦改变就不能再改。把这条判断写进 XHR 的 loadend,再用 Promise 串起 then 和 catch,就完成了“获取省份列表 + 异常提示”的完整流程。

课件图示 ​

课件第20页:核心功能 Promise + XHR 获取省份数据并渲染 请求数据 传递数据 接

课件第22页:异常处理 判断请求状态 传递异常信息 接收并处理异常 const p = new

代码示例 ​

示例 1:Promise 基础用法与三种状态(定时器模拟异步) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>01-Promise 基础用法与状态</title>
</head>
<body>
  <script>
    // 第 1 步:实例化 Promise 对象,回调函数接收 resolve / reject 两个函数参数
    const p = new Promise((resolve, reject) => {
      // 这个回调在实例化时"立刻执行",里面写异步操作
      setTimeout(() => {
        // 第 2 步:异步成功 -> 调用 resolve,状态 pending 变成 fulfilled,结果传给 then
        resolve('成功了,很开心');

        // 异步失败 -> 调用 reject,状态 pending 变成 rejected,结果传给 catch
        // reject('嘤嘤嘤失败了');
      }, 1000);
    });

    // 第 3 步:then 接收成功结果(对应 resolve),catch 接收失败结果(对应 reject)
    p.then((res) => {
      console.log('then 拿到成功结果:', res);
    }).catch((err) => {
      console.log('catch 拿到失败结果:', err);
    });

    // 状态验证:实例化后同步打印,此时还是 pending(待定)
    console.log('同步打印状态(pending):', p);

    setTimeout(() => {
      console.log('1 秒后状态:', p); // fulfilled(已兑现)或 rejected(已拒绝)
    }, 1500);

    // 状态一旦敲定就不可再改变:先 reject 再 resolve,resolve 无效
    const p2 = new Promise((resolve, reject) => {
      reject('第一次就失败了');   // 状态敲定为 rejected
      resolve('我又成功了');      // 不会生效,状态无法再改变
    });
    p2.then((res) => console.log('p2-then:', res))
      .catch((err) => console.log('p2-catch:', err));
    setTimeout(() => console.log('p2 最终状态:', p2), 100);
  </script>
</body>
</html>

关键行与易错点

  • new Promise((resolve, reject) => {...}):回调里的代码在实例化时立刻执行,不是等 then 才执行;resolve / reject 是浏览器传进来的函数,不是自己定义的。
  • 对应关系不能记反:resolve(成功)→ then;reject(失败)→ catch。这和 axios 的 .then / .catch 是同一套机制。
  • 三种状态:pending(待定,实例化后的默认值)→ fulfilled(已兑现,调 resolve 时)/ rejected(已拒绝,调 reject 时)。
  • 易错点:状态一旦改变就敲定,不可逆。先调 reject 再调 resolve,后者不生效,then 也不会执行;控制台展开 Promise 对象即可看到 PromiseState。
  • 易错点:console.log(p) 在同步代码里看到的是 pending,想看结果状态要放进定时器/回调里再打印。

示例 2:Promise + XHR 获取省份列表(核心功能) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>02-Promise + XHR 获取省份列表</title>
</head>
<body>
  <div class="box"></div>

  <script>
    // 1. 创建 Promise 对象,用 Promise 管理 XHR 异步任务
    const p = new Promise((resolve, reject) => {
      // 1.1 请求数据:XHR 代码写在回调函数里,实例化时就会发请求
      const xhr = new XMLHttpRequest();
      xhr.open('GET', 'https://hmajax.itheima.net/api/province'); // 换成你课程资料里的省份列表接口地址

      xhr.addEventListener('loadend', () => {
        // response 是 JSON 字符串,先转成 JS 对象再往外传,外部就不用再转
        const data = JSON.parse(xhr.response);
        // 1.2 传递数据:成功调用 resolve
        resolve(data);
      });

      xhr.send();
    });

    // 2. 接收并使用数据:resolve 后状态变成 fulfilled,触发 then 的回调
    p.then((res) => {
      console.log('接收到的数据:', res);        // 打印出来是个对象,要的是 res.list
      document.querySelector('.box').innerHTML = res.list;
    });
  </script>
</body>
</html>

关键行与易错点

  • XHR 的三件套顺序不能乱:open → 注册 loadend 事件 → send();忘了 xhr.send() 请求根本不会发出去,这是最常见的漏写。
  • 数据必须在 loadend 事件里取,用 xhr.response;此时拿到的是 JSON 字符串,所以用 JSON.parse 转成对象后再 resolve,让外部直接可用。
  • 易错点:把 XHR 代码写在 new Promise 回调外面。写在回调里,才能用 resolve 把结果“抛”给 then。
  • res.list 是数组,直接赋给 innerHTML 会按逗号拼接显示;想换行可以 res.list.join('<br>')。
  • 接口地址请以你课程接口文档中的省份列表接口为准,替换上方 URL 即可。

示例 3:Promise + XHR 异常处理(状态码判断 + reject 传递错误) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>03-Promise + XHR 异常处理</title>
</head>
<body>
  <div class="box"></div>

  <script>
    const p = new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      // 故意写错地址,演示 404 异常(正确地址去掉 123)
      xhr.open('GET', 'https://hmajax.itheima.net/api/province123');

      xhr.addEventListener('loadend', () => {
        // 2.1 判断请求状态:2xx(200~299)才算成功
        if (xhr.status >= 200 && xhr.status < 300) {
          const data = JSON.parse(xhr.response);
          resolve(data);                    // 成功:状态 -> fulfilled,触发 then
        } else {
          // 2.2 传递异常信息:用 Error 对象包一层再 reject
          reject(new Error(xhr.response));  // 失败:状态 -> rejected,触发 catch
        }
      });

      xhr.send();
    });

    p.then((res) => {
      document.querySelector('.box').innerHTML = res.list;
    }).catch((err) => {
      // 2.3 接收并处理异常
      console.log('catch 里的 err:', err);  // 直接 log 看不到 message 里的内容
      console.dir(err);                      // 以对象形式打印,能看到 message 属性
      document.querySelector('.box').innerHTML = err.message;
    });
  </script>
</body>
</html>

关键行与易错点

  • xhr.status 是响应状态码(本次新学的属性):200~299 为成功,404 是找不到资源。判断条件写成 >= 200 && < 300,不要写成 === 200,也不要写成 <= 300。
  • 易错点:JSON.parse 只能放在成功分支里。404 时 xhr.response 返回的是一段 HTML 错误提示,直接 parse 会抛语法错误。
  • reject(new Error(xhr.response)):传的是 Error 对象而不是裸字符串,这样 catch 里才能用 err.message 取到文本。
  • 易错点:console.log(err) 看到的是 Error 对象本身,属性内容要展开或改用 console.dir(err) 才能看到 message。
  • 渲染错误提示用 innerHTML 赋 err.message(纯文本);若赋整段 xhr.response 会连 HTML 标签一起渲染进盒子。

常见错误 ​

1. 在 Promise 回调外调用 resolve/reject

  • 现象:控制台报 resolve is not defined,或异步结果永远拿不到,then 里的回调不执行。
  • 原因:resolve/reject 是 new Promise((resolve, reject) => {...}) 回调函数的形参,只在回调函数作用域内有效;写到外面或忘记声明形参都用不了。
  • 修法:确认构造函数内的回调形参写成 (resolve, reject),并把 resolve(...)/reject(...) 的调用写在回调函数体内(通常位于定时器、XHR 的回调里)。

2. 状态改变后再次调用 resolve/reject,误以为还能切换成功/失败

  • 现象:先调用 reject 再调用 resolve,then 的回调始终不执行,Promise 状态仍是 rejected。
  • 原因:Promise 状态一旦从 pending 变为 fulfilled 或 rejected 就不可逆,后续调用被忽略。
  • 修法:把成功/失败判断集中在一次 resolve/reject 调用上(如用 if (xhr.status >= 200 && xhr.status < 300) 二选一分支),不要指望“后写覆盖先写”。

3. 只写 then 不写 catch,异常信息拿不到

  • 现象:请求地址写错时页面无任何提示,控制台可能只有 XHR 的红色报错,自己的失败处理逻辑没跑。
  • 原因:reject 传递的失败结果只能被 catch(或 then 的第二个参数)接收,只写 then 时失败分支无人处理。
  • 修法:链式补上 .catch(err => {...});并在 XHR 的 loadend 里按状态码判断,失败时 reject(new Error(xhr.response))。

4. 用 console.log 打印 Error 对象,看不到 message

  • 现象:console.log(err) 输出一个 Error 对象,展开后找不到想渲染的提示文本。
  • 原因:console.log 对 Error 对象的展示不展开自有属性,需换用能展开对象的输出方式。
  • 修法:改用 console.dir(err) 查看 message 属性,页面渲染时直接取 err.message 而不是整个 err。

5. 状态码判断写成 === 200,把其他成功响应当失败

  • 现象:接口返回 201/204 时走进 catch 分支,页面显示异常信息。
  • 原因:HTTP 成功是 200–299 的区间,不是单一值。
  • 修法:写成 xhr.status >= 200 && xhr.status < 300,或用 xhr.status >= 200 && xhr.status <= 299。

6. 在 XHR 回调里直接 return 数据,而不调用 resolve

  • 现象:then 里拿到的是 undefined。
  • 原因:return 只结束当前回调函数,不会把值交给 Promise;只有 resolve(值) 才会把结果传给 then。
  • 修法:在 loadend 中 resolve(JSON.parse(xhr.response)),把解析后的数据作为成功结果传出。

练习 ​

1. 定时器版 Promise 基础 用 new Promise 包装一个 1 秒定时器,成功时传出字符串“任务完成”,失败时传出“任务失败”;外部用 then/catch 分别打印。要求:先测成功,再注释掉 resolve 测失败。

2. 状态观察实验 实例化一个只写了 setTimeout 的 Promise(不调用 resolve/reject),在浏览器控制台打印该对象并查看 PromiseState;然后在定时器里分别调用 resolve、reject,再各看一次状态值,记录三次结果。

3. 不可逆验证 写一个 Promise:定时器里先 resolve('A'),紧接着再 reject('B')。观察 then 与 catch 哪个执行、控制台打印的状态是什么,并用一句话解释原因。

4. Promise 封装 XHR 获取省份列表 用 new Promise 包裹 XMLHttpRequest,请求省份列表接口,成功时 resolve(JSON.parse(xhr.response).list),失败时 reject(new Error(xhr.response));外部 then 中把数组渲染到 .box 容器,catch 中把 err.message 渲染到同一容器。

5. 异常分支改造 把第 4 题中的地址故意改错(如末尾加 123),要求:在 loadend 里先打印 xhr.status,用状态码区间判断走 resolve 还是 reject,并在 catch 里分别用 console.log(err) 和 console.dir(err) 对比输出差异。


原视频对照 ​

#视频时长
1认识-Promise08:16
2了解-Promise的状态09:53
3案例-使用Promise加XHR获取省份列表-核心功能09:12
4案例-使用Promise加XHR获取省份列表-异常处理10:44

小结 ​

  1. Promise 是浏览器内置对象,用来管理异步操作的最终状态与结果值,then 接成功、catch 接失败,与 axios 的用法一脉相承。
  2. 使用固定三步:new Promise((resolve, reject) => {...}) 实例化 → 在回调内执行异步任务并用 resolve/reject 传出结果 → 外部 .then()/.catch() 接收处理。
  3. 三种状态 pending / fulfilled / rejected 与代码一一对应:实例化即 pending,调用 resolve 转 fulfilled,调用 reject 转 rejected。
  4. 状态改变是触发回调的唯一开关:状态不变则 then/catch 都不会执行;状态一旦敲定就不可逆,重复调用被忽略。
  5. 用 Promise 管理 XHR 时,XHR 代码写在构造函数回调内,resolve/reject 写在 loadend 里,send() 不能漏。
  6. 判断请求成功用状态码区间 200–299,而不是等于 200;失败时用 reject(new Error(xhr.response)) 包装成错误对象传出。
  7. 调试 Error 对象用 console.dir 看 message 属性,页面渲染时取 err.message 而非整个对象。
  8. 把定时器换成 XHR 后,resolve/reject 的传递逻辑完全不变——这正是 Promise 能统一管理各类异步任务的体现。