Promise 系列 | 开篇 - 你对 Promise 的基本特性了解多少
前言
用 Promise 处理异步请求在代码里都是老熟人了,不过很多情况下都是别人帮我们封装好的,我们只需要调用 then
、catch
、finally
这些对象方法处理结果就能完成工作。
但是我们自己想用好 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):
succCal
和 failCal
,分别用来处理 请求成功 的结果和 请求失败 的结果,改造 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);
})
虽然我们解决了异步请求取值的问题,但是上述方案仍有一些 弊端:
-
当一个请求需要的参数依赖于另一个请求的结果时,会产出过多的 异步嵌套 ,就会出现 回调地狱(callback hell) 的问题,导致代码可读性差,难以维护
-
你还需要给成功、失败回调函数选取合适的命名,且过多重复操作
-
在团队中,自己封装不够清晰,缺少统一的规范
ES6 - Promise
通过 new
创建 promise 对象时,传入回调函数 executor
const promise = new Promise((resolve, reject) => {});
其中,(resolve, reject)=> {}
,并在函数中有两个回调函数 resolve
,reject
用于处理成功/失败的结果
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 必然是这三种状态的一种: pending
、fulfilled
、rejected
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)
,也就是 resolve 和 reject 这两个函数
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