【JS】Promise的实现和面试常考点

Promise

第一版:没有任何异步逻辑

const PENDING = 'PENDING'

const RESOLVED = 'RESOLVED'

const REJECTED = 'REJECTED'

class Promise {

constructor(executor) {

this.status = PENDING;

this.value = undefined

this.reason = undefined

let resolve = (value) => {

if (this.status === PENDING) {

this.value = value

this.status = RESOLVED

}

}

let reject = (reason) => {

if (this.status === PENDING) {

this.reason = reason

this.status = REJECTED

}

}

try {//为什么加try..catch,因为内部可能直接出错

executor(resolve, reject)//给用户提供两个函数

} catch (e) {

reject(e)

}

}

then(onfulfilled, onrejected) {

if (this.status === RESOLVED) {

onfulfilled(this.value)

}

if (this.status === REJECTED) {

onrejected(this.reason)

}

}

}

module.exports = Promise

第二版:如果executor里面有异步操作,则需要采用发布订阅模式进行处理

const PENDING = 'PENDING'

const RESOLVED = 'RESOLVED'

const REJECTED = 'REJECTED'

class Promise {

constructor(executor) {

this.status = PENDING;

this.value = undefined

this.reason = undefined

// 专门存放成功的回调的函数

this.onResolvedCallbacks = [];

// 专门存放失败的回调函数的

this.onRejectedCallbacks = [];

let resolve = (value) => {

if (this.status === PENDING) {

this.value = value

this.status = RESOLVED

// 当调用resolve时,说明要走成功态,所以依次执行then中成功的回调

this.onResolvedCallbacks.forEach(fn => fn())

}

}

let reject = (reason) => {

if (this.status === PENDING) {

this.reason = reason

this.status = REJECTED

// 当调用reject时,说明要走失败态,所以执行then中失败的回调

this.onRejectedCallbacks.forEach(fn => fn())

}

}

try {

executor(resolve, reject)

} catch (e) {

reject(e)

}

}

then(onfulfilled, onrejected) {

if (this.status === RESOLVED) {

onfulfilled(this.value)

}

if (this.status === REJECTED) {

onrejected(this.reason)

}

//如果有异步操作(如setTimeout),执行then方法时状态还是PENDING,则需要将then中的两个方法先存起来

if (this.status === PENDING) {

//第一种:将这个函数直接扔进去,但是不太好,不方便扩展自己的功能

// this.onResolvedCallbacks.push(onfulfilled)

// this.onRejectedCallbacks.push(onrejected)

// 第二种:使用切片编程,外面包一层,方便扩展

this.onResolvedCallbacks.push(() => {

onfulfilled(this.value)

})

this.onRejectedCallbacks.push(() => {

onrejected(this.reason)

})

}

}

}

module.exports = Promise

第三版:then的链式调用及then是个异步操作

const PENDING = 'PENDING'

const RESOLVED = 'RESOLVED'

const REJECTED = 'REJECTED'

// 一般微任务比宏任务早执行,也不是绝对的,setTimeout是宏任务

// 判断x的状态 是让promise2走成功态还是失败态

function resolvePromise(promise2, x, resolve, reject) {

// TODO

}

class Promise {

constructor(executor) {

this.status = PENDING;

this.value = undefined

this.reason = undefined

this.onResolvedCallbacks = [];

this.onRejectedCallbacks = [];

let resolve = (value) => {

if (this.status === PENDING) {

this.value = value

this.status = RESOLVED

this.onResolvedCallbacks.forEach(fn => fn())

}

}

let reject = (reason) => {

if (this.status === PENDING) {

this.reason = reason

this.status = REJECTED

this.onRejectedCallbacks.forEach(fn => fn())

}

}

try {

executor(resolve, reject)

} catch (e) {

reject(e)

}

}

then(onfulfilled, onrejected) {

// 链式调用,返回的需要是个新的promise

const promise2 = new Promise((resolve, reject) => {

// 将之前判断条件放到这个promise的里面,因为它是promise,所以是立即执行,不影响

if (this.status === RESOLVED) {

//将then执行之后的结果传到下一个promise的resolve或者reject中

// 需要判断这个x是普通值还是promise,如果是promise,则需要让这个promise执行,执行之后的状态将作为promise2的状态

setTimeout(() => {//为什么需要setTimeout?因为官方说了then是个异步操作

try {//为什么要包住,因为执行onfulfilled可能会出错

let x = onfulfilled(this.value)

// 我需要根据x,判断调用promise2的resolve还是reject,所以将promise2和它的resolve和reject传过去

// 此时应该没有promise2,应该怎么搞?答案就是通过添加宏任务(setTimeout)或者微任务(nextTick)

resolvePromise(promise2, x, resolve, reject)

} catch (e) {

reject(e)

}

}, 0)

}

if (this.status === REJECTED) {

setTimeout(() => {

try {

let x = onrejected(this.reason)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

}

if (this.status === PENDING) {

this.onResolvedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onfulfilled(this.value)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

})

this.onRejectedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onrejected(this.reason)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

})

}

})

}

}

module.exports = Promise

第四版:resolvePromise方法的实现

const PENDING = 'PENDING'

const RESOLVED = 'RESOLVED'

const REJECTED = 'REJECTED'

function resolvePromise(promise2, x, resolve, reject) {

// 此方法 为了兼容所有的promise,n个库中间 执行的流程是一样的

// 尽可能详细 不出错

// 1) 不能引用同一个对象 可能会造成死循环

if (promise2 === x) {

return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))

}

let called;

// 2) 判断x的类型 x是对象或者函数,才有可能是promise

if ((typeof x === 'object' && x != null) || typeof x === 'function') {

try {

let then = x.then; // {a:1} 因为then方法 可能使用的getter来定义的

if (typeof then === 'function') { // 只能认为他是promise了

// call 改变this指向 并且让函数执行

then.call(x, y => { // 只取一次 当前promise解析出来的结果可能还是一个promise继续解析直到他是一个普通值为止

if (called) return;

called = true;

// 递归解析resolve的值

resolvePromise(promise2, y, resolve, reject)

}, r => {

if (called) return;

called = true;

reject(r);

})

} else {

// 如果不是函数,则只能是这种对象了{a:1,then:1}

resolve(x)

}

} catch (e) { // 我取then出错了 在错误中又掉了该promise的成功

if (called) return

called = true;

reject(e); //取值失败 就走到error中

}

} else {

// 进到这里说明是普通值,走成功态

resolve(x)

}

}

class Promise {

constructor(executor) {

this.status = PENDING;

this.value = undefined

this.reason = undefined

this.onResolvedCallbacks = [];

this.onRejectedCallbacks = [];

let resolve = (value) => {

if (this.status === PENDING) {

this.value = value

this.status = RESOLVED

this.onResolvedCallbacks.forEach(fn => fn())

}

}

let reject = (reason) => {

if (this.status === PENDING) {

this.reason = reason

this.status = REJECTED

this.onRejectedCallbacks.forEach(fn => fn())

}

}

try {

executor(resolve, reject)

} catch (e) {

reject(e)

}

}

catch(errCallback){ // catch就是没有成功的then方法

return this.then(null,errCallback)

}

then(onfulfilled, onrejected) {

const promise2 = new Promise((resolve, reject) => {

if (this.status === RESOLVED) {

setTimeout(() => {

try {

let x = onfulfilled(this.value)

resolvePromise(promise2, x, resolve, reject)

} catch (e) {

reject(e)

}

}, 0)

}

if (this.status === REJECTED) {

setTimeout(() => {

try {

let x = onrejected(this.reason)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

}

if (this.status === PENDING) {

this.onResolvedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onfulfilled(this.value)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

})

this.onRejectedCallbacks.push(() => {

setTimeout(() => {

try {

let x = onrejected(this.reason)

resolvePromise(x)

} catch (e) {

reject(e)

}

}, 0);

})

}

})

}

}

module.exports = Promise

// npm install -g promises-aplus-tests

常见面试题

  • promise链式调用原理和jquery链式调用原理区别

答:jquery中的链式调用原理是通过返回this实现的,而promise的链式调用是通过返回新的promise实现的

  • Promise.all方法的原理

答:all方法的特点是让所有的promise并发执行,内部使用**计数器**的方式判断是否全部执行完了,内部使用递归

  • Promise.resolve 和 Promise.reject区别

答:

Promise.resolve()参数可以接受一个promise,并且等待它状态改变;

Promise.reject()也可以接受promise,但是不会有等待效果,直接会走失败态

以上是 【JS】Promise的实现和面试常考点 的全部内容, 来源链接: www.h5w3.com/115152.html

回到顶部