Promise入门

发布于:2025-07-20 ⋅ 阅读:(13) ⋅ 点赞:(0)


为什么使用Promise

在ES5中使用回调函数来处理异步任务,当多个异步任务有依赖关系时(如下定时器的层层嵌套),就需要回调函数互相嵌套,当嵌套结构多了后,就出现了回调地狱的问题,难以维护

setTimeout(function () {
    console.log('a1');
    setTimeout(function () {
        console.log('a2');
        setTimeout(function () {
            console.log('a3');
            setTimeout(function () {
                console.log('a4');
                setTimeout(function () {
                    console.log('a5');
                }, 1000);
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);

故在ES6中提供了Promise 对象,解决了以上问题,并统一了规范,提高了可读性等

Promise介绍

Promise有三种状态

  1. pending:等待中。属于初始状态,既没有被兑现,也没有被拒绝。
  2. fulfilled:已兑现/已解决/成功。执行了resolve() 时,立即处于该状态,表示 Promise已经被解决,任务执行成功
  3. rejected:已拒绝/失败。执行了 reject()时,立即处于该状态,表示 Promise已经被拒绝,任务执行失败。
    Promise构造函数如下
// 1. 定义 executor 函数
function executor(resolve, reject) {
  setTimeout(() => {
    resolve("foo");
  }, 300);
}

// 2. 把 executor 传入 Promise 构造函数
const promise1 = new Promise(executor);


//使用箭头函数则是
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

此处的resolve reject是形参,可以任意命名,但通常以此命名
而executor 的函数体(第一个箭头函数的函数体)中负责启动异步任务,并在恰当的时机调用resolvereject
如下

const coinPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (Math.random() < 0.5) {
      resolve(1);
    } else {
      reject('error');
    }
  }, 0);
});

// 使用
coinPromise
  .then(val => console.log(val))   // 可能输出 1
  .catch(err => console.log(err)); // 可能输出 error

then和catch也可以这样写

promise.then(onFulfilled).catch(onRejected);//这是上面的结构
//等价
promise.then(onFulfilled, onRejected);

如上是一个50%的概率输出1或error的promise实例
可以看出调用的resolve()的参数1会被then中匿名箭头函数的参数val接收,同理,error对应err
并且promise的状态一旦改变,则确定了下来,不可逆,
执行机制如图
在这里插入图片描述

而then 方法返回一个新的 Promise,从而允许链式调用。(then返回的对象状态为待定,与当前的Promise的状态无关)
then其中的匿名箭头函数的返回值会被then捕获成为其新的Promise的返回值
故开头的回调可以如下修改

// 1. 先把 setTimeout 包装成返回 Promise 的工具
//计时不需要判断成功与失败,故只传resolve,构造体为setTimeout(resolve, ms)
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// 2. 链式写法,彻底消灭回调地狱
delay(1000)
  .then(() => { console.log('a1'); return delay(1000); })//执行完‘ai’后会成为新的delay(1000)
  .then(() => { console.log('a2'); return delay(1000); })
  .then(() => { console.log('a3'); return delay(1000); })
  .then(() => { console.log('a4'); return delay(1000); })
  .then(() => { console.log('a5'); });

上面的链式结构没有匿名函数的参数,可以使用n记录层数作为参数

const delay = (ms, value) =>
  new Promise(resolve => setTimeout(() => resolve(value), ms));

// 从 0 开始,逐层+1  n会接收resolve的value
delay(1000, 0)               // 第 0 层
  .then(n => { console.log('a' + n); return delay(1000, n + 1); }) // 0 → 1
  .then(n => { console.log('a' + n); return delay(1000, n + 1); }) // 1 → 2
  .then(n => { console.log('a' + n); return delay(1000, n + 1); }) // 2 → 3
  .then(n => { console.log('a' + n); return delay(1000, n + 1); }) // 3 → 4
  .then(n => { console.log('a' + n); });                          // 4 → 5

如下,是抛硬币的层层嵌套,连续5次成功才会正常结束

const flip = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.5) {
        resolve('success');
      } else {
        reject('error');
      }
    }, 0);
  });

flip()
  .then(() => { console.log('成功'); return flip(); })
  .then(() => { console.log('成功'); return flip(); })
  .then(() => { console.log('成功'); return flip(); })
  .then(() => { console.log('成功'); return flip(); })
  .then(() => { console.log('finally成功'); })
  .catch(() => { console.log('失败'); });

其中的return flip();匿名箭头函数的返回值会被then捕获成为其新的Promise的返回值


不仅如此
resolve()的参数不同,会决定对应的Promise的状态

  1. 如果resolve()中传入普通的值或者普通对象(包括 undefined),那么Promise 的状态为fulfilled。这个值会作为then()回调的参数。这是最常见的情况
  2. 如果resolve()中传入的是另外一个新的 Promise,那么原 Promise 的状态将交给新的 Promise 决定。
  3. 如果resolve()中传入的是一个对象,并且这个对象里有实现then()方法(这种对象称为 thenable 对象),那就会执行该then()方法,并且根据then()方法的结果来决定Promise的状态。

此处暂不涉及

故有了promise就可以更简单的处理回调函数的嵌套问题


网站公告

今日签到

点亮在社区的每一天
去签到