认识 Promise

认识 Promise

前言:为什么会出现 Promise?

最常见的一个场景就是 ajax 请求,通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。

在 ajax 的原生实现中,利用了 onreadystatechange 事件,当该事件触发并且符合一定条件时,才能拿到想要的数据,之后才能开始处理数据,这样做看上去并没有什么麻烦,但如果这个时候,我们还需要另外一个 ajax 请求,这个新 ajax 请求的其中一个参数,得从上一个 ajax 请求中获取,这个时候我们就不得不等待上一个接口请求完成之后,再请求后一个接口

js 复制代码
let xhr = new XMLHttpRequest();
xhr.open('get', 'https://...');
xhr.send();
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log(xhr.responseText)

      //伪代码....
      let xhr = new XMLHttpRequest();
      xhr.open('get','http://www.xx.com?a'+xhr.responseText);
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
          if(xhr.status>=200 && xhr.status<300){
            console.log(xhr.responseText)
          }
        }
      }
    }
  }
}

当出现第三个 ajax(甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。

这场灾难,往往也被称为回调地狱

因此我们需要一个叫做 Promise 的东西,来解决这个问题,当然,除了回调地狱之外,还有个非常重要的需求就是:为了代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来

上面的写法,是完全没有区分开,当数据变得复杂时,也许我们自己都无法轻松维护自己的代码了。这也是模块化过程中,必须要掌握的一个重要技能,请一定重视。

1. Promise 是什么?

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。

ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题

旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs 模块、ajax、数据库操作

  • 从语法上说,Promise 是一个构造函数;
  • 从功能上说,Promise 对象用来封装一个异步操作并可以获取其成功 / 失败的结果值。

2. Promise 的优点

  1. 指定回调函数的方式更加灵活

    1. 旧的方法:必须在启动异步任务前指定

    2. promise:启动异步任务 -> 返回 promise 对象 -> 给 promise 对象绑定回调函数

      (甚至可以在异步任务结束后指定多个)

  2. 可以解决回调地狱问题,支持链式调用

    1. 什么是回调地狱?

      回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

    2. 回调地狱的缺点?

      不便于阅读、不便于异常处理

    3. 解决方案?

      promise链式调用

    4. 终极解决方案

      async / await

3. 定义 Promise 对象

js 复制代码
new Promise((resolve, reject) => { ... });

3.1 Promise 实例对象的两个属性

  1. PromiseState

    此属性为 promise 对象的状态属性。

    • fulfilled:成功的状态
    • rejected:失败的状态
    • pending:初始化的状态

    【注】状态只能由 pending -> fulfilled 或是 pending -> rejected

  2. PromiseResult

    此属性为 promise 对象的结果值(resolve 以及 reject 函数的形参值)

3.2 Promise 实例对象的两个参数

  1. resolve

    修改 promise 对象的状态,由 pending 修改为 fulfilled;

    将实参设置到这个属性 PromiseResult 中。

  2. reject

    修改 promise 对象的状态,由 pending 修改为 rejected;

    将实参设置到这个属性 PromiseResult 中。

js 复制代码
let p = new Promise((resolve, reject) => {
  // 调整状态
  // reject(new Error("error")); // 状态为 rejected
  resolve("success"); // 状态为 resolved
});
console.log(p); // Promise { <pending> }

4. Promise 对象的状态

Promise 对象通过自身的状态来控制异步操作,Promise 实例具有三种状态.

  1. 异步操作未完成:pending
  2. 异步操作成功:fulfilled
  3. 异步操作失败:rejected

这三种的状态的变化途径只有两种

  • 从 pending(未完成)到 fulfilled(成功)
  • 从 pending(未成功)到 rejected(失败)

一旦状态发生变化,就凝固了,不会再有新的状态变化,这也是 Promise 这个名字的由来,它的英语意思 "承诺",一旦承诺生效,就不得再改变了,这也意味着 Promise 实例的状态变化只可能发生一次

在 Promise 对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理 Promise 的状态变化。

上面的 resolve 和 reject 都为一个函数,他们的作用分别是将状态修改为 resolved 或 rejected。

因此,Promise 的最终结果只有两种情况:

  1. 异步操作成功,Promise 实例传回一个值(value),状态变为 fulfilled;
  2. 异步操作失败,Promise 实例抛出一个错误(error),状态变为 rejected

5. then 方法(重要)

实例化 Promise 时,使用回调函数作为参数,回调函数通常有两个参数:

  1. resolve 参数

    当执行到 resolve( ... ) 时,会调用 then 方法中的第一个参数(回调);

  2. reject 参数

    当执行到 reject( ... ) 时,会调用 then 方法中的第二个参数(回调);

then 方法中通常有两个回调函数作为参数,第一个回调在成功时(resolve)调用,第二个回调在出错时(reject)调用,第二个参数可以省略。

5.1 then 方法返回结果

调用 then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定

  1. 返回结果是非 Promise 类型的属性

    返回状态 resolved(成功),返回值为对象成功的值

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        return 123;
      },
      (error) => { console.warn(error); }
    );
    
    console.log(result); // 返回值为 123

    如果未使用 return 进行返回,则返回值为 undefined。

  2. 返回 Promise 对象

    返回值和返回状态均由返回的 promise 对象的返回值和状态决定

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        return new Promise((resolve, reject) => {
          resolve("ok");
          // reject("出错了");
        });
      },
      (error) => { console.warn(error); }
    );
    console.log(result); // 返回状态为 resolved,返回值为 ok
    // console.log(result); // 返回状态为 rejected,返回值为 出错了
  3. 抛出错误

    返回状态 rejected(失败)

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        // throw new Error("出错了");
        throw "出错了";
      },
      (error) => { console.warn(error); }
    );
    
    console.log(result); // 返回状态为 rejected,返回值为 出错了

5.2 then 方法的链式调用

由于 promise 可以返回 promise 对象,因此可以进行链式调用

js 复制代码
// 链式调用
p.then(
  (data) => {},
  (error) => {}
).then(
  (data) => {},
  // 失败回调可以省略
)...;

下一节:Promise 详细说明

相关推荐
seabirdssss9 小时前
通过动态获取项目的上下文路径来确保请求的 URL 兼容两种启动方式(IDEA 启动和 Tomcat 部署)下都能正确解析
java·okhttp·tomcat·intellij-idea
ps酷教程11 小时前
Apache httpclient & okhttp(2)
okhttp·apache
ps酷教程3 天前
OkHttp&HttpClient
okhttp·httpclient
东望5 天前
从基础用法到源码实现:手写 Promise 的完整指南
javascript·promise
东东__net7 天前
27_promise
okhttp
阿湯哥7 天前
SSE SseEmitter.completeWithError(e) 触发的处理逻辑
okhttp
朝与暮9 天前
Promise详解(包含手写promise)
前端·promise
JuliusDeng11 天前
Promise的2025版
promise
每次的天空12 天前
Android第六次面试总结(okhttp篇)
android·okhttp