面试被摁在地上摩擦,面试官:手写一个promise吧。我:我不太了解会用不会写。。。。。
找到痛点,实现一下吧。。。
promise的使用
const text = new Promise((resolve, reject) => {
resolve("我好菜");
});
text.then((res) => {
console.log(res)
}, (err) => {
console.log(err)
});
复制代码
promise的实现
首先通过上面的使用方法所知,promise是一个类,那我们就用class来定义它
class TestPromise {
constructor() {}
}
复制代码
总所周知,promise是有三个状态的,那我们先定义一下,而且要给他一个初始的状态,
const RESOLVE = 'resolve',
const REJECT = 'reject',
const PENDING = 'pending',
class TestPromise {
status = PENDING;
constructor() {}
}
复制代码
promise 会接收一个函数作为参数,并且传入后会立即执行
constructor(excution) {
excution()
}
复制代码
函数会带有两个参数resolve,reject,所以我们要定义一下
const RESOLVE = 'resolve',
const REJECT = 'reject',
const PENDING = 'pending',
class TestPromise {
status = PENDING;
result = undefined;
reason = undefined;
constructor(excution) {
// 调用resolve和reject的时候要传参, 参数默认为undefined
const resolve = (result) => {
}
const reject = (reason) => {
}
excution(resolve, reject)
}
}
复制代码
我们知道promise的状态一经改变是无法更改的,所以在进入对应函数的时候要更改相应的状态
const resolve = (result) => {
if(this.status === PENDING) {
this.result = result;
this.status = RESOLVE;
}
}
const reject = (reason) => {
if(this.status === PENDING) {
this.result = reason;
this.status = REJECT;
}
}
复制代码
然后看下我们调用的then方法,先在代码中给他定义出来
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
}
复制代码
传入的方法是需要运行的, 第一个参数和第二个参数分别为不同状态的时候去运行
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
onResolved(this.result)
}
if (this.status === REJECT) {
onReject(this.result)
}
}
复制代码
初版差不多出来了, 长这个样子
const RESOLVE = 'resolve',
const REJECT = 'reject',
const PENDING = 'pending',
class TestPromise {
status = PENDING;
result = undefined;
reason = undefined;
constructor(excution) {
// 调用resolve和reject的时候要传参, 参数默认为undefined
const resolve = (result) => {
if(this.status === PENDING) {
this.result = result;
this.status = RESOLVE;
}
}
const reject = (reason) => {
if(this.status === PENDING) {
this.result = reason;
this.status = REJECT;
}
}
excution(resolve, reject);
}
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
onResolved(this.result)
}
if (this.status === REJECT) {
onReject(this.result)
}
}
}
复制代码
验证一下
const newTest = new TestPromise((resolve, reject) => {
resolve('啦啦啦啦啦');
});
const text = new Promise((resolve, reject) => {
resolve("我好菜");
});
text.then((res) => {
console.log(res)
}, (err) => {
console.log(err)
});
newTest.then((res) => {
console.log(res);
}, (err) => {console.log(err)})
复制代码
打印结果是出来了 但是感觉好像有点问题, 我的promise好像不是异步的。。。。搞一下,修改then中的方法
then(onResolved, onReject) {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
setTimeout(() => {
onResolved(this.result)
}, 0)
}
if (this.status === REJECT) {
setTimeout(() => {
onReject(this.result)
})
}
}
复制代码
ok! 实现了!由于原生的promise是v8引擎提供的微服务,我们无法实现v8引擎的服务,所以这里用setTimeout来实现异步
异步调用
现在我们已经实现了一个简单的promise,但是我们传入一个异步的操作会发生什么呢 试一下
const newTest = new TestPromise((resolve, reject) => {
setTimeout(() => {
resolve('啦啦啦啦啦');
}, 1000)
});
newTest.then((res) => {
console.log(res);
}, (err) => {console.log(err)})
复制代码
可以看到控制台并没有结果返回
因为promise在执行then方法的时候,当前的promise并没有成功,一直处于pending状态中,所以如果调用then方法时,状态是pending,我们需要先将成功和失败的回调分别存放起来,在异步任务执行时触发resolve或者reject,依次调用成功和失败的回调
改进下代码
const RESOLVE = 'resolve';
const REJECT = 'reject';
const PENDING = 'pending';
class TestPromise {
status = PENDING;
result = undefined;
reason = undefined;
// 新增存放成功回调的数组
onResolveCallbackArr = [];
// 新增存放失败回调的数组
onRejectCallbackArr = []
constructor(excution) {
// 调用resolve和reject的时候要传参, 参数默认为undefined
const resolve = (result) => {
if(this.status === PENDING) {
this.result = result;
this.status = RESOLVE;
// 遍历数组执行操作
this.onResolveCallbackArr.forEach(fn => fn());
}
}
const reject = (reason) => {
if(this.status === PENDING) {
this.result = reason;
this.status = REJECT;
// 遍历数据执行操作
this.onRejectCallbackArr.forEach(fn => fn());
}
}
excution(resolve, reject);
}
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
setTimeout(() => {
onResolved(this.result)
}, 0)
}
if (this.status === REJECT) {
setTimeout(() => {
onReject(this.result)
}, 0)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolveCallbackArr.push(() => {
setTimeout(() => {
onResolved(this.result)
}, 0)
});
this.onRejectCallbackArr.push(() => {
setTimeout(() => {
onReject(this.result)
}, 0)
})
}
}
}
const newTest = new TestPromise((resolve, reject) => {
setTimeout(() => {
resolve('啦啦啦啦啦');
}, 1000)
});
newTest.then((res) => {
console.log(res);
}, (err) => {console.log(err)})
复制代码
这样,异步的方式就完成了。
我们是通过使用发布订阅模式来完成异步的调用功能,发布订阅模式就是收集依赖 -> 触发通知 -> 取出依赖执行
的这样一个过程。
链式调用
promise是可以支持链式调用的 所以我们的.then 每次返回都需要是一个promise,搞一下
then(onResolved, onReject) {
// 建立一个promise
let promiseTwo = new TestPromise((resove, reject) => {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
setTimeout(() => {
onResolved(this.result)
}, 0)
}
if (this.status === REJECT) {
setTimeout(() => {
onReject(this.result)
}, 0)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolveCallbackArr.push(() => {
setTimeout(() => {
onResolved(this.result)
}, 0)
});
this.onRejectCallbackArr.push(() => {
setTimeout(() => {
onReject(this.result)
}, 0)
})
}
})
// 返回一个promise
return promiseTwo;
}
复制代码
然后测试下。。
const newTest = new TestPromise((resolve, reject) => {
setTimeout(() => {
resolve('啦啦啦啦啦');
}, 1000)
});
newTest.then((res) => {
console.log(res);
}, (err) => {console.log(err)}).then((res) => {
console.log(res, '哈哈哈哈')
}, (err) => {console.log(err)})
复制代码
链式调用的第二个没有打印出来,虽然我们已经return了一个promise 但是 promise 里的resolve没有触发。。。。 那我们就写个方法让他去触发这个事件
const RESOLVE = 'resolve';
const REJECT = 'reject';
const PENDING = 'pending';
const hendlePromise = (result, newPromise, resolve, reject) => {
// 结果等于自身没有意义
if (result === newPromise) {
throw new Error('can not return oneself')
}
// 判断是不是一个Promise是的话要处理 不是的话直接返回值
if ((typeof result === 'object' && typeof result !== null) || typeof result === 'function') {
// 看看有么有then方法
const then = result.then;
// 入果有then方法,并且是个函数的话,我们就认为他是个promise,
if (typeof then === 'function') {
then.call(result, (r) => {
hendlePromise(r, newPromise, resolve, reject)
}, (e) => {
reject(e)
})
} else {
resolve(result);
}
} else {
resolve(result)
}
}
class TestPromise {
status = PENDING;
result = undefined;
reason = undefined;
// 新增存放成功回调的数组
onResolveCallbackArr = [];
// 新增存放失败回调的数组
onRejectCallbackArr = []
constructor(excution) {
// 调用resolve和reject的时候要传参, 参数默认为undefined
const resolve = (result) => {
if(this.status === PENDING) {
this.result = result;
this.status = RESOLVE;
// 遍历数组执行操作
this.onResolveCallbackArr.forEach(fn => fn());
}
}
const reject = (reason) => {
if(this.status === PENDING) {
this.result = reason;
this.status = REJECT;
// 遍历数据执行操作
this.onRejectCallbackArr.forEach(fn => fn());
}
}
excution(resolve, reject);
}
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
// 建立一个promise
let promiseTwo = new TestPromise((resove, reject) => {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
setTimeout(() => {
const t = onResolved(this.result)
hendlePromise(t, promiseTwo, resove, reject)
}, 0)
}
if (this.status === REJECT) {
setTimeout(() => {
const t = onReject(this.result)
hendlePromise(t, promiseTwo, resove, reject)
}, 0)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolveCallbackArr.push(() => {
setTimeout(() => {
const t = onResolved(this.result)
hendlePromise(t, promiseTwo, resove, reject)
}, 0)
});
this.onRejectCallbackArr.push(() => {
setTimeout(() => {
const t = onReject(this.result)
hendlePromise(t, promiseTwo, resove, reject)
}, 0)
})
}
})
// 返回一个promise
return promiseTwo;
}
}
const newTest = new TestPromise((resolve, reject) => {
setTimeout(() => {
resolve('啦啦啦啦啦');
}, 1000)
});
newTest.then((res) => {
console.log(res);
return '完成'
}, (err) => {console.log(err)}).then((res) => {
console.log(res, '哈哈哈哈')
}, (err) => {console.log(err)})
复制代码
看下运行结果
剩下我们就需要在代码中新增try catch方法为了防止输入出错–看下完整的代码
const RESOLVE = 'resolve';
const REJECT = 'reject';
const PENDING = 'pending';
const hendlePromise = (result, newPromise, resolve, reject) => {
// 结果等于自身没有意义
if (result === newPromise) {
throw new Error('can not return oneself')
}
// 新增lock属性,保证状态只更改一次
let lock;
// 判断是不是一个Promise是的话要处理 不是的话直接返回值
if ((typeof result === 'object' && typeof result !== null) || typeof result === 'function') {
try {
// 看看有么有then方法
const then = result.then;
// 入果有then方法,并且是个函数的话,我们就认为他是个promise,
if (typeof then === 'function') {
then.call(result, (r) => {
if (lock) return;
lock = true;
hendlePromise(r, newPromise, resolve, reject)
}, (e) => {
if (lock) return;
lock = true;
reject(e)
})
} else {
resolve(result);
}
} catch (error) {
if (lock) return;
lock = true;
reject(error)
}
} else {
resolve(result)
}
}
class TestPromise {
status = PENDING;
result = undefined;
reason = undefined;
// 新增存放成功回调的数组
onResolveCallbackArr = [];
// 新增存放失败回调的数组
onRejectCallbackArr = []
constructor(excution) {
// 调用resolve和reject的时候要传参, 参数默认为undefined
const resolve = (result) => {
if(this.status === PENDING) {
this.result = result;
this.status = RESOLVE;
// 遍历数组执行操作
this.onResolveCallbackArr.forEach(fn => fn());
}
}
const reject = (reason) => {
if(this.status === PENDING) {
this.result = reason;
this.status = REJECT;
// 遍历数据执行操作
this.onRejectCallbackArr.forEach(fn => fn());
}
}
try {
excution(resolve, reject);
} catch (error) {
reject(error)
}
}
// 调用then方法, 接收两个方法!
then(onResolved, onReject) {
// 建立一个promise
let promiseTwo = new TestPromise((resove, reject) => {
// 传入的方法需要运行,根据不同的状态运行不同的方法
if (this.status === RESOLVE) {
setTimeout(() => {
try {
const t = onResolved(this.result)
hendlePromise(t, promiseTwo, resove, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === REJECT) {
setTimeout(() => {
try {
const t = onReject(this.result)
hendlePromise(t, promiseTwo, resove, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolveCallbackArr.push(() => {
setTimeout(() => {
try {
const t = onResolved(this.result)
hendlePromise(t, promiseTwo, resove, reject)
} catch (error) {
reject(error)
}
}, 0)
});
this.onRejectCallbackArr.push(() => {
setTimeout(() => {
try {
const t = onReject(this.result)
hendlePromise(t, promiseTwo, resove, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
// 返回一个promise
return promiseTwo;
}
}
const newTest = new TestPromise((resolve, reject) => {
setTimeout(() => {
resolve('啦啦啦啦啦');
}, 1000)
});
newTest.then((res) => {
console.log(res);
return '完成'
}, (err) => {console.log(err)}).then((res) => {
console.log(res, '哈哈哈哈')
}, (err) => {console.log(err)})
复制代码
promiseAPI
catch
catch(errCallback) {
return this.then(null, errCallback)
}
复制代码
resolve
static resolve(data) {
return new TestPromise((resolve, reject) => {
if (data instanceof TestPromise) {
return data.then(resolve, reject)
}
if (this.status === PENDING) {
this.status = RESOLVE;
this.reason = data;
this.onResolveCallbackArr.forEach(fn => fn())
}
resolve(data)
})
}
复制代码
reject
static reject(reason){
return new TestPromise((resolve,reject)=>{
reject(reason);
})
}
复制代码
all
static all(values) {
return new TestPromise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === values.length) {
resolve(resultArr)
}
}
for (let i = 0; i < values.length; i++) {
let value = values[i];
if (value && typeof value.then === 'function') {
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
})
}
复制代码
race
static rece(values) {
return new TestPromise((resove, reject) => {
values.forEach((item) => {
if (item && typeof item.then === 'function') {
item.then(resove, reject)
} else {
resove(item)
}
})
})
}
复制代码