关于Promise
在日常工作中 Promise 可以说是随处可见,和 async/await 配合使用更是如虎添翼,能让代码具有更好的可读性,理解Promise的运行机制对前端来说能帮我们写出更加优雅的代码。
Promise 的用法
const promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
});
- Promise 的构造函数接受一个函数作为参数,这个函数接受两个参数resolve 和 reject,执行resolve 会将promise 置为fulfilled(成功)状态,reject 会将promise 置为rejected(失败)状态。
- then 函数接受两个参数:then(onResolved,onRejected), onResolved 是Promise 成功的回调,onReject 是Promise 失败时的回调。
Promise 对象上不仅有 then 还有catch finally
这几个函数有几个特点
- then/catch/finally 均返回一个新的Promise对象,这也就是promise可以链式调用的原因
- 前一个then/catch/finally,有可能返回的还是一个
Promise
对象(即有异步操作),这时后一个回调函数then/catch,就会等待该Promise
对象的状态发生变化,才会被调用。 - catch 只接受一个参数,等效与 then(undefined,onRejected)
- finally 只是负责透传上一个promise的值,除非finally 中的代码报错
接下来我们看几个例子从而加深对这几句话的理解
const promise1 = new Promise(()=>{
throw new Error('初始化Promise时报错')
})
const promise2 = promise1.then(()=>{
console.log('then1 触发');
})
const promise3 = promise2.then(()=>{
console.log('then2 触发')
}).catch((error)=>{
console.log(error);
})
// 更加常用的写法法是直接连接多个then,这里为了后续说明问题方便采用拆开的方式书写
执行上述代码最终输出
Error: 初始化Promise时报错
at <anonymous>:2:8
at new Promise (<anonymous>)
at <anonymous>:1:1
如果讲上述代码用一张图表示可以表示为下面这张图
实现一个Promise
如何实现一个Promise 就不过多赘述了,可以跟着这边文章手写一遍,能够帮助我们更好的理解Promise的外在表现
9k字 | Promise/async/Generator实现原理解析 - 掘金 (juejin.cn)
这里贴上Promise的实现代码
function microtask(fn) {
return (...args) => {
let counter = 1;
const observer = new MutationObserver(() => {
fn(...args);
});
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
timerFunc();
};
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
export default class MyPromise {
_status = PENDING;
_value = undefined;
_reason = undefined;
_resolveQueue = [];
_rejectQueue = [];
constructor(executor) {
// 构造resolve
const _resolve = microtask((value) => {
if (this._status !== PENDING) return;
this._status = FULFILLED;
this._value = value;
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift();
callback(value);
}
});
// 构造reject
const _reject = microtask((reason) => {
if (this._status !== PENDING) return;
this._status = REJECTED;
this._reason = reason;
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback(reason);
}
});
try {
executor(_resolve, _reject);
} catch (error) {
console.log('executor err', error);
_reject(error);
}
}
then(onFulfilled, onRejected) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => value;
}
// 需要把reject 透传下去
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
throw new Error(
reason instanceof Error ? reason.message : reason
);
};
}
return new MyPromise((resolve, reject) => {
const createCallback = (callback, next) => (value) => {
try {
const result = callback(value);
if (result instanceof MyPromise) {
result.then(next, reject);
} else {
debugger;
next(result);
}
} catch (error) {
debugger;
reject(error);
}
};
const resolveFn = createCallback(onFulfilled, resolve);
const rejectFn = createCallback(onRejected, reject);
switch (this._status) {
case PENDING:
this._resolveQueue.push(resolveFn);
this._rejectQueue.push(rejectFn);
break;
case FULFILLED:
resolveFn(this._value);
break;
case REJECTED:
rejectFn(this._reason);
break;
}
});
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(callback) {
const newPromise = this.then(
(value) => {
return MyPromise.resolve(callback()).then(
() => value,
(err) => {
throw err;
}
);
},
(reason) => {
debugger;
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
return newPromise;
}
static resolve(value) {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise((resolve) => resolve(value));
}
static reject(reason) {
if (reason instanceof MyPromise) {
return reason;
}
return new MyPromise((resolve, reject) => reject(reason));
}
}
Promise 链式调用的原理
我们简单分析一下promise的链式调用的原理以及状态是如何一步步透传的。
如果将上述代码用一张图表示可以表示为:
每一次执行then 都会产生一个新的Promise,onFulfilled是promise成功的回调,onRejected 是执行失败的回调,当内部的/前一个Promise的状态发生改变时会通知外部Promise 以此类推,从而实现链式调用并且结果以此向外传递。
在上一个例子中promise1 的error 可以传递到最后一个是因为,onRejected 的默认值是
onRejected = (reason) => {
throw new Error(
reason instanceof Error ? reason.message : reason
);
};
所以会将错误一直向外传递直到被捕获。