闲来无事,想来一个面试题系列,先来一个最经典的手写的一个Promise,你会吗?我不会,所以我来写一个。。。
1. Promise详解
想手写实现一个Promise先要知道Promise的原理和他内部的流程,就是重要知道自己要写什么,就和写业务拆分页面,拆解组件一个道理。
我们熟知的Promise就是一个异步对象,可以传入一个带有resolve, reject参数的函数如下:
const pro = new Promise((res, rej) => {
console.log('promise执行了')
})
复制代码
Promise里还有自己的状态,Padding 为初始的状态,Fulfilled 为成功的状态,Rejected 为失败的状态,这里的状态是不可逆的,就是一个Promise对象状态一旦发生改变就不会再转换回去,这里其实就为我们后续调用的函数提供了保证。(PS: Promise对象里的代码是同步执行的)
不同状态有与之对应的实例方法来执行后续的逻辑,promise.then():获取异步任务的正常结果,promise.catch():获取异步任务的异常结果,promise.finaly():异步任务无论成功与否,都会执行。
const pro = new Promise((res, rej) => {
console.log('promise执行了')
res('成功')
// rej('失败')
})
pro
.then(res => {
console.log(res)
})
.catch(rej => {
console.log(rej)
})
.finally(() => {
console.log('完成')
})
复制代码
Promise还有一个很重要的链式调用,就是Promise的返回都是一个Promise对象,可以继续执行后续的状态方法的调用。如果逻辑需要中断可以在需要中断的地方手动的抛出一个throw错误来中断链式调用。
还有两个使用的方法,
promise.all (): 并发处理多个异步任务,所有任务都执行成功,才能得到结果
promise.race (): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果
以上就是Promise的内容,也可以说是这次的功能点拆分,下面就是咱们自己手写一个Promise的环节了。
2. 手写Promise
- 首先我们写一个myPromise类,先实现传进一个函数参数,传入就立即执行
class myPromise{
constructor(immediate) {
// 成功
let resolve = () => {}
// 失败
let reject = () => {}
// 立即执行函数
immediate()
}
}
复制代码
- 接着定义myPromise里的三个状态pengding, fulfilled, rejected
class myPromise {
constructor(immediate) {
this.state = 'pending'
this.value // 成功时的值
this.error // 失败时的值
// 成功
let resolve = (res) => {
if (this.state === 'pending') {
this.state = 'fufilled'
this.value = res
}
}
// 失败
let reject = (rej) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.error = rej
}
}
// 立即执行函数
immediate(resolve, reject)
}
}
new myPromise((resolve, reject) => {
resolve('成功了')
})
// 控制台输出 Promise {state: "fufilled", value: "成功了"}
复制代码
- 接下来实现.then方法
class myPromise {
constructor(immediate) {
this.state = 'pending'
this.value // 成功时的值
this.error // 失败时的值
// 成功
let resolve = (res) => {
if (this.state === 'pending') {
this.state = 'fufilled'
this.value = res
}
}
// 失败
let reject = (rej) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.error = rej
}
}
// 立即执行函数
immediate(resolve, reject)
}
// 声明then方法
then (fufilledCall, rejectedCall) {
if (this.state === 'fufilled') {
fufilledCall(this.value)
}
if (this.state === 'rejected') {
rejectedCall(this.error)
}
}
}
new myPromise((resolve, reject) => {
resolve('成功了')
})
.then(res => {
console.log('成功回调', res)
})
// 控制台输出 成功回调 成功了
复制代码
- 然后实现.then链式调用(完整代码)
class myPromise {
constructor(immediate) {
this.state = 'pending'
this.value // 成功时的值
this.error // 失败时的值
// 成功
let resolve = (res) => {
if (this.state === 'pending') {
this.state = 'fufilled'
this.value = res
}
}
// 失败
let reject = (rej) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.error = rej
}
}
// 立即执行函数
immediate(resolve, reject)
}
// 声明then方法
then (fufilledCall, rejectedCall) {
let promise2
promise2 = new myPromise((resolve, reject) => {
if (this.state === 'fufilled') {
let x = fufilledCall(this.value);
resolvePromise(promise2, x, resolve, reject)
}
if (this.state === 'rejected') {
let x = rejectedCall(this.error);
resolvePromise(promise2, x, resolve, reject)
}
})
return promise2
}
}
/**
* 处理promise递归的函数
*
* promise2 {Promise} 默认返回的promise
* x {*} 我们自己 return 的对象
* resolve
* reject
*/
function resolvePromise (promise2, x, resolve, reject) {
// 循环引用报错
if (x === promise2) {
// reject 报错抛出
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 锁,防止多次调用
let called;
// x 不是 null 且 x 是对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+ 规定,声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// then 执行 第一个参数是 this 后面是成功的回调 和 失败的回调
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// 核心点2:resolve 的结果依旧是 promise 那就继续递归执行
resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err);// 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) { // 走到 catch 也属于失败
if (called) return;
called = true;
// 取then出错了那就不要在继续执行了
reject(e);
}
} else {
resolve(x);
}
}
new myPromise((resolve, reject) => {
resolve('成功了')
})
.then(res => {
console.log('成功回调', res)
return new myPromise((resolve, reject) => {
resolve('hello world');
})
})
.then(res => {
console.log('成功回调2', res)
return new myPromise((resolve, reject) => {
resolve('haha')
})
})
.then(res => {
console.log('成功回调3', res)
})
复制代码
这里手写一个Promise基本完成,但是每次都需要在.then里手动return一个对象出来,感觉不完美,还没想到好方法解决,在看看吧。
有好的方法也可以发出来,让我学习一下。