「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
Promise是什么和Promise的基础用法
Promise是什么
Promise简单来说的话,可以理解成一个容器,里面放着某个未来才会结束的操作(一般是异步的)的结果。从javascript的语法来看,Promise是一个对象,它包含了异步操作的信息,并提供了统一的API,各类异步操作都能用相同的方式进行处理。
Promise的基础用法
最简单的用法如下,封装了一个读取文件的函数,把异步读取的内容在then里传递下去。
function readFile(name) {
return new Promise((resolve, reject) => {
fs.readFile(name, 'utf8', (error, data) => {
if(error) reject(error);
resolve(data);
});
});
}
readFile(A).then(data => {
return readFile(B);
}).then(data => {
return readFile(C);
}).then(data => {
return readFile(D);
}).catch(reason => {
console.log(reason);
});
Promise解决了什么问题
在这里扩展一点,为什么需要Promise这种异步处理的方式呢?其实就是为了解决以前callback处理异步导致的回调地狱问题。
let readFilePromise = readFile(A).then(data => readFile(B)); // 这里返回了一个promise
readFilePromise.then(/* 回调内部逻辑省略 */) // 回调函数延迟传入
- 首先异步之后的回调不再是直接被申明的,而是通过后面的then方法传入,即延迟传入。
- 根据then中回调函数的传入值创建新的Promise,然后把返回的Promise穿透到外层,以供后续的调用。这里的readFilePromise指的就是内部返回的Promise,然后在readFilePromise后面可以依次完成链式调用。这便是then的链式调用和返回值穿透的效果。
- Promise采用错误冒泡的方式处理执行过程中发生的报错。这样前面产生的错误会一直向后传递,直到被catch方法接收到,避免在每个回调里重复检查错误。 这样就解决了多层嵌套的问题,代码显得更清爽,并且更符合人的线性思维。
Promise的手写实现
在手动实现 Promise 之前,还必须先了解 Promise/A+ 规范。只有对规范有了明确的认知才能更好的实现Promise。
Promise/A+ 规范
- Promise是一个具有then方法的函数或者对象,这个then方法可以用来访问它的值或者拒绝原因。
- Promise内部有value代表返回值和reason代表拒绝原因。
- 一个Promise内部有三种状态,分别是:pending、fulfilled 和 rejected;一开始状态为pending,可以转换为fulfilled或者rejected其中之一;当状态已为fulfilled状态时,就不能转换为其他状态了,且必须返回一个不能再改变的值value;当状态已为rejected状态时,同样也不能转换为其他状态,且必须返回一个不能再改变的值reason代表原因。
- then的链式调用:我们使用Promise时候,每次then执行后必须返回一个新的Promise对象,并且当then函数中返回了任意的值value,我们能再下一个then方法中获取到。
- 返回值穿透特性:当我们使用then,没往里传入参数的时候,比如promise1.then().then(),后面执行的then函数依旧可以获得之前then函数返回的值。具体实现如下,更多的细节我会在代码的注释里给出。
一步步实现Promise
- 实现构造函数。 定义3个常量作为Promise的状态值;初始化status为PENDING状态、value返回值、reason拒绝原因、onResolvedCallbacks储存then方法传入的onFulfilled函数、onRejectedCallbacks储存then方法传入的onRejected函数;再定义个resolve方法为改变promise的状态值并赋值返回值value、执行onResolvedCallbacks数组里的函数;再定义个reject方法为改变promise的状态值并赋值返回值reason、执行onRejectedCallbacks数组里的函数。最后直接在构造函数中执行new Promise里传入的函数,并把定义的resolve和reject函数传入。
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class ThePromise {
constructor(fn) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
const reject = () => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
fn(resolve, reject);
}catch(error) {
reject(error);
}
}
}
- 实现then方法。 then方法需要实现链式调用和返回值穿透特性。实现方案:每次调用then方法的时候,都新建一个promise对象,并把上一次的返回的值传给这个新的promise的then方法里,这样就可以无限链式调用了。更多的细节写在代码的注释里。
then(onResolved, onRejected) {
// 用于then()的情况下返回值穿透到下一次then
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// 抛出错误,冒泡出去
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error; };
// 每次调用then都返回一个新的promise,用于链式调用
const anotherPromise = new ThePromise((resolve, reject) => {
// 如果promise的状态已经确定并且是FULFILLED,我们调用onResolved,
// 考虑到有可能throw,所以还需要将其包在try/catch块里
if(this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onResolved(this.value);
// 递归处理,x可能是一个promise
resolvePromise(anotherPromise, x, resolve, reject);
} catch(error) {
reject(error);
}
},0);
}
// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数
if(this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(anotherPromise, x, resolve, reject);
} catch(error) {
reject(error);
}
},0);
}
// 如果当前的Promise还处于PENDING状态,我们并不能确定调用onResolved还是onRejected,
// 只能等到Promise的状态确定后,才能确定如何处理
if(this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
try {
const x = onResolved(this.value);
resolvePromise(anotherPromise, x, resolve, reject);
}catch(error) {
reject(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolvePromise(anotherPromise, x, resolve, reject);
}catch(error) {
reject(error);
}
});
}
}
return anotherPromise;
}
function resolvePromise(anotherPromise, x, resolve, reject) {
// 如果执行回调后的值还是promise自己,则报错
if(anotherPromise === x) {
return reject(new TypeError('Wrong implementation'));
}
let called; // 用于确定promise的状态只改变一次
if(x instanceof ThePromise){
try {
x.then(f => {
if(called) return;
called = true;
// 递归解析
resolvePromise(anotherPromise, f, resolve, reject);
}, r => {
if(called) return;
called = true;
reject(r);
});
}catch(error) {
if(called) return;
called = true;
reject(error);
}
}else{
// 如果x不是promise实例则直接resolve
resolve(x);
}
}