首先分类讨论
定出大纲,promise的状态,异步函数的回调执行,链式调用。
Promise的状态
Promise在英文当中语义承诺,承诺是不允许被更改的,在Promise当中亦是如此。 总共有三种状态, fulfilled成功 rejected失败 pending执行中
一开始是pending然后才会变成fulfilled和rejected状态
异步执行
如何异步执行,我们使用setTimeOut(()=>{},0) 进行模拟异步
如何链式调用
进行.then(()=>{}).then(()=>{})这种链式调用
.then需要对then里面的内容进行判断是不是函数
如果是函数就拿到参数执行对于的函数
把这个值保存道全局的this.PromiseResult中
这样下一个.then就可以拿到上一个.then中处理完的结果进行处理
实现链式调用
伪代码
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
}
开始实现代码
状态实现
const statusMap = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
}
使用对象存储状态,便于统一管理和后期的拓展
实现promise类
我们先看整体,首先肯定有一个resolve方法和reject方法让我们去进行调用.
在constructor里面也是接受一个fn方法
里面将resolve和reject方法当作参数进行传入
constructor初始化操作
initValue方法是用来初始化值和状态
initBind是用来绑定this指向 在函数调用的时候绑定的this始终指向hdPromise实例
代码
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
this.PromiseResult = null
this.PromiseState = statusMap.PENDING
}
简单代码
这里就不过多解释了,大家有点基础的肯定都懂。
const statusMap = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
}
class hdPromise {
constructor(fn) {
this.initValue()
this.initBind()
fn(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
this.PromiseResult = null
this.PromiseState = statusMap.PENDING
}
resolve(value) {
this.PromiseState = statusMap.FULFILLED
this.PromiseResult = value
}
reject(reason) {
this.PromiseState = statusMap.REJECTED
this.PromiseResult = reason
}
}
问题1
状态没有不可变
下面一段的是代码执行结果从fulfilled变成了rejected
const test = new hdPromise((resolve, reject) => {
resolve('成功')
reject('失败')
})
console.log(test)
执行结果
解决
加if进行判断即可
resolve(value) {
if (this.PromiseState !== statusMap.PENDING) return
this.PromiseState = statusMap.FULFILLED
this.PromiseResult = value
}
reject(reason) {
if (this.PromiseState !== statusMap.PENDING) return
this.PromiseState = statusMap.REJECTED
this.PromiseResult = reason
}
问题2
捕捉错误执行reject
Promise中有throw的话,会去执行reject。使用try catch进行错误捕获
代码
try {
fn(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
执行.then
需要完成的任务
then接收两个回调,一个是成功回调,一个是失败回调 根据对应状态执行
如果resolve或reject在定时器里,则定时器结束后再执行then
then支持链式调用,下一次then执行受上一次then返回值的影响
支持定时器
如果then里面有setTimeOut定时器500ms后执行
如何保证then会在500ms后进行处理里面的回调函数
那怎么知道有没有执行定时器呢,只要状态是pending说明还没有进行执行
可以使用数组来进行保存对应的回调函数
实现then方法
then(onFulfilled, onRejected) {
onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (val) => val
onRejected = this.isFunction(onRejected)
? onRejected
: (err) => {
throw err
}
if (this.PromiseState === statusMap.FULFILLED) {
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === statusMap.REJECTED) {
onRejected(this.PromiseResult)
} else if (this.PromiseState === statusMap.PENDING) {
this.resolveStack.push(onFulfilled.bind(this))
this.rejectStack.push(onRejected.bind(this))
}
}
其他方法
initValue() {
this.PromiseResult = null
this.PromiseState = statusMap.PENDING
this.resolveStack = []
this.rejectStack = []
}
resolve(value) {
if (this.PromiseState !== statusMap.PENDING) return
this.PromiseState = statusMap.FULFILLED
this.PromiseResult = value
while (this.resolveStack.length) {
this.resolveStack.shift()(this.PromiseResult)
}
}
reject(reason) {
if (this.PromiseState !== statusMap.PENDING) return
this.PromiseState = statusMap.REJECTED
this.PromiseResult = reason
while (this.rejectStack.length) {
this.rejectStack.shift()(this.PromiseResult)
}
}
链式调用
then支持链式调用,下一次then执行受上一次then返回值的影响
关键知识点
1、then方法本身会返回一个新的Promise对象
2、如果返回值是promise对象,返回值为成功,新promise就是成功
3、如果返回值是promise对象,返回值为失败,新promise就是失败
4、如果返回值非promise对象,新promise对象就是成功,值为此返回值
then(onFulfilled, onRejected) {
onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (val) => val
onRejected = this.isFunction(onRejected) ? onRejected : (err) => { throw err }
const thenResult = new HdPromise((resolve, reject) => {
const resolvePromise = (cb) => {
try {
const res = cb(this.PromiseResult)
if (res === thenResult) {
throw new Error('请不要返回自身')
}
if (res instanceof HdPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (err) {
reject(err)
}
}
if (this.PromiseState === statusMap.FULFILLED) {
resolvePromise(onFulfilled)
} else if (this.PromiseState === statusMap.REJECTED) {
resolvePromise(onRejected)
} else if (this.PromiseState === statusMap.PENDING) {
this.resolveStack.push(resolvePromise.bind(this, onFulfilled))
this.rejectStack.push(resolvePromise.bind(this, onRejected))
}
})
return thenResult
}
细节讲解
通过res instanceof HdPromise 进行判断是不是HdPromise实例
来决定可不可以执行res.then
使用resolvePromise 去处理回调函数
整体进行包装成HdPromise来进行返回 return出去
一些边界条件处理
res === thenResult
返回结果不能是自己
catch (err) 处理错误
微任务-异步执行
使用setTimeout进行模拟
常见手写体all-race
实际面试当中让你手写Promise是很少的
但是手写all和race是很多的
这里有一定的技巧
手写all
代码
var a = new Promise(resolve=>{
setTimeout(()=>{
console.log(1)
resolve(1)
},1000)
})
var b = new Promise(resolve=>{
setTimeout(()=>{
console.log(2)
resolve(2)
},2000)
})
function all(promises) {
let len = promises.length, res = []
console.log(len,promises,'promises')
if (len) {
return new Promise(function (resolve, reject) {
for(let i=0; i < len; i++){
let promise = promises[i];
promise.then(response => {
res[i] = response
// 当返回结果为最后一个时
if (res.length === len) {
resolve(res)
}
}, error => {
reject(error)
})
}
})
}
}
all([a,b]).then(res=>{console.log(res,'res')})
细节
就是把所有的promise执行结果都放入一个数组当中
如果数组长度等于所有的promise数组长度说明执行完毕
可以进行resolve(res)出去
通过res[i] = 执行结果 我们就可以保证结果的执行顺序
自身包裹成一个Promise进行返回
手写race
race就更简单直接resolve出去最快的哪一个就可以
代码
function race(promises) {
let len = promises.length, res = []
if (len) {
return new Promise(function (resolve, reject) {
for(let i=0; i < len; i++){
let promise = promises[i];
promise.then(res => {
resolve(res)
}, error => {
reject(error)
})
}
})
}