JS-异步编程的终极解决方案:Promise

51 阅读5分钟

前言

在 JavaScript 的世界里,异步操作无处不在。从早期的“回调地狱(Callback Hell)”到现在的线性逻辑,Promise 的出现彻底改变了我们编写异步代码的方式。它不仅是一个构造函数,更是一种流程控制的哲学。

一、 为什么需要 Promise?(背景与痛点)

  1. 告别回调地狱:回调函数里面嵌套过多回调函数,导致的回调地狱问题,而Promise通过回调函数延迟绑定和回调函数返回值穿透的技术,解决了回调地狱问题。
  2. 线性逻辑:异步函数过多时,导致的代码逻辑不连贯,Promise封装了异步代码,让处理流程变得线性。

二、 Promise 的灵魂:三种状态与两个特性

Promise 就像是一个状态机,它的核心在于状态的不可逆性。

1. 三种状态

  • Pending(进行中) :初始状态,异步操作尚未完成。
  • Fulfilled(已成功) :操作成功完成,通常通过调用 resolve() 触发。
  • Rejected(已失败) :操作失败,通常通过调用 reject() 触发。

2. 核心特性

  • 不可逆性:状态只能从 Pending 变为 FulfilledRejected。一旦变为 Resolved(已定型) ,状态就不会再变。
  • 结果持久:如果你在状态改变之后再添加回调,回调函数依然会立即得到结果。

三、 Promise 的优缺点辩证

  • 优点:代码结构化、支持链式调用、错误处理统一。

  • 缺点

    • 无法中途取消:一旦 new 出来就会立即执行。
    • 错误黑盒:如果不设置回调,内部抛出的错误不会反应到外部。
    • 状态模糊:处于 Pending 时,无法得知进度(是刚开始还是快结束了)。

四、 核心用法全拆解

1. 构造函数是“同步”执行的!

注意:在实例化 Promise 时传入的函数会立即执行。

new Promise(function(resolve, reject) {
    console.log("我会立即执行"); // 同步执行
    resolve("成功");
});
console.log("我在 Promise 后面");
// 输出顺序:我会立即执行 -> 我在 Promise 后面

2. resolve 与 reject 的博弈

resolvereject是传入function的两个参数,这两个参数也是函数。

  • 当我们在function中调用resolve方法时,Promise实例对象的状态就变为fulfilled(操作成功)。当Promise状态变为fulfilled时,就会执行Promise实例对象里面的then方法方法里面的onfulfiled方法
  • 当我们在function中调用reject方法时,Promise实例对象的状态就变为rejected(操作失败)。当Promise状态变为reject,就会执行Promise实例对象里面的then方法里面的onrejected方法,或者执行Promise实例对象里面的catch方法

注意:当在function里面调用resolvereject时,先调用的那个函数会执行,后调用的不会执行了,因为Promise实例对象的状态一旦改变就不会再改变

var p = new Promise(function (resolve, reject) {
  var timer = setTimeout(function () {
    console.log("执行操作1");
    resolve("这是数据1");
    console.log("执行操作2");
    reject("这是数据2");
  }, 1000);
});
p.then(
  function (data) {
    console.log(data);
    console.log("这是成功操作");
  },
  function (data) {
    console.log(data);
    console.log("这是失败操作");
  }
);
//执行操作1   执行操作2   这是数据1   这是成功操作

五、 then 与 catch 的执行潜规则

  • then里面包含两个参数:onfulfilledonrejected,这两个参数也是方法

    • onfulfilled:当Promise实例对象的状态为fulfilled会执行这个方法
    • onrejected:当Promise实例对象的状态为rejected会执行这个方法
    // resolve例子
    var p = new Promise(function (resolve, reject) {
      var timer = setTimeout(function () {
        console.log("执行操作1");
        resolve("这是数据1");
      }, 1000);
    });
    p.then(
      function (data) {
        console.log(data);
        console.log("这是成功操作");
      },
      function (data) {
        console.log(data);
        console.log("这是失败操作");
      }
    );
    //执行操作1     这是数据1        这是成功操作
    
    // rejected例子
    var p = new Promise(function (resolve, reject) {
        var timer = setTimeout(function () {
            console.log('执行操作2');
            reject('这是数据2');
        }, 1000);
    });
    p.then(function (data) {
                console.log(data);
                console.log('这是成功操作');
            },
            function (data) {
                console.log(data);
                console.log('这是失败操作');
            });
    //执行操作2     这是数据2        这是失败操作
    
  • catch方法其实和then方法里面第二个参数一样,当Promise实例对象的状态为rejected会执行这个方法。

  • 注意:如果then方法里面onrejected方法和cathch方法同时存在,则分为两种情况:

    • catch方法紧跟在then方法后面:只执行than方法里面的onrejected方法
    • catch方法单独写在外面:两个都执行,先执行then方法里面onrejected方法,再执行catch方法
var p = new Promise(function (resolve, reject) {
    var flag = false;
    if(flag){
      resolve('这是数据2');
    }else{
      reject('这是数据2');
    }
  });
p.then(function(data){
      console.log(data);
      console.log('这是成功操作');
  },function(data){
     console.log(data)
     console.log('这是失败操作1')
  }).catch(function(reason){
    console.log(reason);
    console.log('这是失败操作2');
});
//这是数据2    这是失败操作1   
var p = new Promise(function (resolve, reject) {
    var flag = false;
    if(flag){
      resolve('这是数据2');
    }else{
      reject('这是数据2');
    }
  });
p.then(function(data){
      console.log(data);
      console.log('这是成功操作');
  },function(data){
     console.log(data)
     console.log('这是失败操作1')
  })
p.catch(function(reason){
    console.log(reason);
    console.log('这是失败操作2');
});
//这是数据2    这是失败操作1   这是数据2  这是失败操作 2

六、 实战:看代码说结果

Promise.resolve(1)
  .then((res) => {
    console.log(res);
    return 2;
  })
  .then((res) => {
    console.log(res);
  })
  .then((res) => {
    console.log(res);
    return Promise.resolve(3);
  })
  .then((res) => {
    console.log(res);
  });

输出结果: 1, 2, undefined, 3

解析:

  1. 第一个 then 接收 1,返回 2(这个 2 会被包装成 Promise.resolve(2))。
  2. 第二个 then 接收 2,打印 2注意:这里没写 return,默认返回 undefined
  3. 第三个 then 接收 undefined,打印 undefined,返回 Promise.resolve(3)
  4. 第四个 then 接收 3,打印 3