likes
comments
collection
share

Promise 系列 | 开篇 - 你对 Promise 的基本特性了解多少

作者站长头像
站长
· 阅读数 47

前言

用 Promise 处理异步请求在代码里都是老熟人了,不过很多情况下都是别人帮我们封装好的,我们只需要调用 thencatchfinally 这些对象方法处理结果就能完成工作。

但是我们自己想用好 Promise 还是有必要花点时间学习一下的,并且学会 Promise 对 掌握回调函数 也有很大帮助。

还有原因是今年打算准备面试,顺便整理一下复习的内容,所以 Promise 相关文章我打算分类写,精准打击,这篇也是 Promise 集合的开篇。

早期异步请求的处理方式

假设,我们希望通过 requestData() 获取一些后端返回的数据

function requestData(age) {
  // 模拟 api 请求
  setTimeout(() => {
    return age >= 18 ? 200 : 500
  }, 2000)
}

requestData(18)  // undefined

由于 api 请求(setTimeout)需要时间,是异步执行;而我们通过 requestData(18) 无法即时得到 return 返回的数据

为了解决这个问题,我们可以传入两个回调函数(callback):

succCalfailCal,分别用来处理 请求成功 的结果和 请求失败 的结果,改造 requestData() 如下:

function requestData(age, succCal, failCal) {
  // 模拟 api 请求
  setTimeout(() => {
    return age >= 18 ? succCal(200) : failCal(500)
  }, 2000)
}

requestData(18, res => {
  console.log(res);  // 2s 后返回 -> 200
}, err => {
  console.log(err);
})

虽然我们解决了异步请求取值的问题,但是上述方案仍有一些 弊端

  1. 当一个请求需要的参数依赖于另一个请求的结果时,会产出过多的 异步嵌套 ,就会出现 回调地狱(callback hell) 的问题,导致代码可读性差,难以维护

  2. 你还需要给成功、失败回调函数选取合适的命名,且过多重复操作

  3. 在团队中,自己封装不够清晰,缺少统一的规范

ES6 - Promise

通过 new 创建 promise 对象时,传入回调函数 executor

const promise = new Promise((resolve, reject) => {});

其中,(resolve, reject)=> {},并在函数中有两个回调函数 resolvereject 用于处理成功/失败的结果

1. Promise 创建后会立即执行

Promise 在创建后就会立即执行,即作为参数传进去的函数会 立即执行

    const promise = new Promise((resolve, reject) => {
        console.log('创建了一个Promise');
        resolve('success');
    });
    
    promise.then(res => {
        console.log('Promise 返回值:' + res);
    });
    
    console.log('随便执行的非异步操作');
    
    // 输出 👇
    // 创建了一个Promise
    // 随便执行的非异步操作
    // Promise 返回值:success

🐝 then 方法指定的回调函数会在 同步任务 之后执行。

2. Promise 的三种状态

一个 Promise 必然是这三种状态的一种: pendingfulfilledrejected

pending 是初始状态,表示挂起,即 Promise 在进行中;

调用 resolve 后,状态由 pending 转变为 fulfilled: 表示 执行成功

调用 reject 后,状态由 pending 转变为 rejected: 表示 执行失败

如果 promise 已经执行成功或失败,也可以说它是 已敲定(settled)  状态

    const p1 = new Promise((resolve, reject) => {
        resolve(1);  // fulfilled
    });

    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => resolve(2), 500);  // pending
    });
    
    const p3 = new Promise((resolve, reject) => {
        setTimeout(() => reject(3), 500);  // pending
    });

    p1.then(value => console.log(value));
    p2.then(value => console.log(value));
    p3.catch(err => console.log(err));
    
    console.log(p1);
    console.log(p2);
    console.log(p3);
    
    setTimeout(() => console.log(p2), 1000);
    setTimeout(() => console.log(p3), 1000);
    
    // 输出 👇
    // Promise {<fulfilled>: 1}
    // Promise {<pending>}
    // Promise {<pending>}
    // 1
    // 2
    // 3
    // Promise {<fulfilled>: 2}
    // Promise {<rejected>: 3}

3. Promise 的不可逆性

Promise 的状态改变只有两种可能:(1) pending -> fulfilled (2) pending -> rejected

并且 Promise 的状态一旦改变,就不会再次改变

// 初始状态 💧
const p1 = new Promise((resolve, reject) => {
    resolve('🧊我的状态改变了~');
    resolve('🍦我决定换个状态!');
});

const p2 = new Promise((resolve, reject) => {
    resolve('🧊我的状态改变了~');
    reject('💧刚才骗你的,状态改变失败了');
});

p1.then(value => console.log(value));  //  🧊我的状态改变了~
p2.then(value => console.log(value));  //  🧊我的状态改变了~

Promise 构造函数

上文中使用 new 创建的 promise 对象,是利用了 Promise 构造器 new Promise(executor)

在 MDN 中,executor 的函数是这样的:

function (resolutionFunc, rejectionFunc) {
  // 通常是一些异步操作
}

接下来我们主要讲一下,当 promise 被敲定(执行成功)时调用 resolutionFunc(value);被拒绝(执行失败)时调用 rejectionFunc(reason),也就是 resolvereject 这两个函数

1. resolve

resolve(value),value 的值可以是以下的 3 种情况

a) 普通的值或者对象

new Promise((resolve, reject) => {
  resolve(200)  // fulfilled
}).then(res => {
  console.log(res);  // 200
})

b) resolve 的值可以还是一个 Promise

如果 resolve 的值仍是一个 Promise 时,最终状态由传入的 Promise 决定

const newPromise = new Promise((resolve, reject) => {
  reject(500)  // rejected
})

new Promise((resolve, reject) => {
  resolve(newPromise)
}).then(res => {
  console.log('res: ' + res);
}, err => {
  console.log('err: ' + err);  // err: 500
})

c) 一个有 then 方法的对象

当 resolve 中的值是一个实现了 thenable 接口的对象时,直接返回一个普通的值,状态也会由 pending 转变为 fulfilled

new Promise((resolve, reject) => {
  const obj = {
    then: (resolve, reject) => {
      resolve(200)
    }
  }
  resolve(obj)
}).then(res => {
  console.log(res);  // 200
})

2. reject

reject 在 promise 拒绝时被调用,通常用来返回 拒绝的原因

new Promise((resolve, reject) => {
  reject('认证失败')  // rejected
}).then(res => {
  console.log(res);
}, err => {
  console.log('reason: ' + err);  // reason: 认证失败
  return err  // 可以继续当作一个 promise 返回,不过通常没什么用
}).then(res => {
  console.log('value: ' + res);  // value: 认证失败
})

一般在执行失败就不会进行后续的操作了,即便你在失败后传入了一个新的 promise,也不会和 resolve 一样获取到值,像下面这样:

const newPromise = new Promise((resolve, reject) => {
  resolve(500)
})

new Promise((resolve, reject) => {
  reject(newPromise)
}).then(res => {}, err => {
  console.log('reason: ' + err);  // reason: [object Promise]
  return err
}).then(res => {
  console.log('value: ' + res);  // value: 500
})

结语

到这里我们简单认识了 Promise 的一些特性和构造函数的基础知识。

感谢小伙伴们能看到这里,下一节我会继续写 Promise 的 三个对象方法六个类方法,以及怎样利用这些方法写出易于维护,简洁的异步代码。

转载自:https://juejin.cn/post/7242336247879106618
评论
请登录