offic做网站的软件,可视化信息 网站,中国菲律宾友谊,石嘴山网站seo前端面试题库 #xff08;面试必备#xff09; 推荐#xff1a;★★★★★
地址#xff1a;前端面试题库
前言
面试官#xff1a;请手写一个Promise#xff1f;#xff08;开门见山#xff09;
我#xff1a;既然说到Promise#xff0c;那我肯定得先介…前端面试题库 面试必备 推荐★★★★★
地址前端面试题库
前言
面试官请手写一个Promise开门见山
我既然说到Promise那我肯定得先介绍一下JavaScript异步编程的发展史吧这样就理解为啥Promise会出现以及Promise解决了什么问题了吧。
阶段一回调函数阶段二事件发布/订阅模型...
面试官我不关心什么异步编程发展史不耐烦这年头谁都知道Promise是解决了回调地狱的问题我关心的是你的编码能力你直接show you code直接写
我好吧其实手写代码才是我的强项嘻嘻
手写promise
先说下promise的三种状态
PENDING等待态promise的初始状态FULFILLED成功态promise调用resolve函数后即会从PENDING等待态变为FULFILLED成功态。REJECTED失败态promise调用reject函数后即会从PENDING等待态变为REJECTED失败态 注意 promise的状态一旦发生变更便无法再更改。比如调用resolve从PEDING变为FULFILLED,它的状态就永远是FULFILLED了再调用reject也无法从FULFILLED变成REJECTED状态只能从PENDING变为FULFILLED或REJECTED不能从FULFILLED或REJECTED返回到PENDING这个也很好理解状态只能前进不能倒退。 先看用法
const p new Promise((resolve, reject) {resolve(111);
})
p.then((value) {console.log(value)
}, (error) {console.log(error)
})首先Promise肯定是一个类所以我们才可以new它然后Promise实例化的时候给它传入一个回调我们叫它executor方法Promise内部会立即调用这个executor方法并且会传入resolve和reject两个函数作为调用参数另外在Promise类的原型上应该提供一个then方法它里面可以传入两个回调分别为Promise成功的回调和Promise失败的回调。调用resolve后会走入成功的回调中调用reject后会走入失败的回调中。
const PENDING PENDING
const FULFILLED FULFILLED
const REJECTED REJECTEDclass Promise {constructor(executor) {this.value undefinedthis.reason undefinedthis.status PENDINGconst resolve (value) {if (this.status PENDING) {this.value valuethis.status FULFILLEDthis.onResolvedCallbacks.forEach(fn fn())}}const reject (reason) {if (this.status PENDING) {this.reason reasonthis.status REJECTEDthis.onRejectedCallbacks.forEach(fn fn())}}executor(resolve, reject);}then(onFulfilled, onRejected) {if (this.status FULFILLED) {onFulfilled onFulfilled(this.value)}if (this.status REJECTED) {onRejected onRejected(this.reason)}}
}module.exports Promise;面试官如果是异步调用resovle或者reject呢
我简单用两个数组充当队列把then里边的回调存起来不就好了。 class Promise {constructor(executor) {// ...// 定义两个数组this.onResolvedCallbacks [];this.onRejectedCallbacks [];const resolve (value) {if (this.status PENDING) {this.value valuethis.status FULFILLEDthis.onResolvedCallbacks.forEach(fn fn())}}const reject (reason) {if (this.status PENDING) {this.reason reasonthis.status REJECTEDthis.onRejectedCallbacks.forEach(fn fn())}}// 默认执行executor函数并传入resolve和reject函数executor(resolve, reject)}then(onFulfilled, onRejected) {if (this.status FULFILLED) {onFulfilled onFulfilled(this.value)}if (this.status REJECTED) {onRejected onRejected(this.reason)}if (this.status PENDING) {this.onResolvedCallbacks.push(() {onFulfilled(this.value)})this.onRejectedCallbacks.push(() {onRejected(this.reason)})}}
}这里定义了两个数组onResolvedCallbacks和onRejectedCallbacks分别存储 then 里面成功的回调和失败的回调然后再调用resolve和reject时分别循环执行这两个数组里存储的回调函数。
面试官可以那promise的链式调用是怎么实现的呢
比如下面这段代码
const p new Promise((resolve, reject) {setTimeout(() {resolve(111)}, 1000)
})
p.then((value1) {console.log(value1, value1)return 222
}, (error1) {console.log(error1, error1)
}).then((value2) {console.log(value2, value2)}, (error2) {console.log(error2, error2)
})
它的打印结果为 这个是如何实现的呢
我这个也简单它内部调用then方法时返回了一个新的promise并让这个新的promise接管了它下一个then方法。 注意这里不能返回this这样会导致多个then方法全部受同一个promise控制。 class Promise {// ...then(onFulfilled, onRejected) {const promise2 new Promise((resolve, reject) {if (this.status FULFILLED) {// onFulfilled方法可能返回值或者promiseconst x onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)}if (this.status REJECTED) {// onRejected方法可能返回值或者promiseconst x onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)}if (this.status PENDING) {this.onResolvedCallbacks.push(() {const x onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)})this.onRejectedCallbacks.push(() {const x onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)})}})return promise2}
}
最核心的就是resolvePromise来看下它做了什么
function resolvePromise(promise2, x, resolve, reject) {if (promise2 x) {return reject(new TypeError(UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #Promise))}let called// 判断x的类型 x是对象或函数才有可能是一个promiseif (typeof x object x ! null || typeof x function) {try {const then x.thenif (typeof then function) {// 只能认为它是一个promisethen.call(x, (y) {if (called) returncalled trueresolvePromise(promise2, y, resolve, reject)}, (r) {if (called) returncalled truereject(r)})}else {resolve(x)}} catch (e) {if (called) returncalled truereject(e)}} else {resolve(x)}
}首先先判断新返回的一个promisepromise2是不是等于x抛出错误UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #Promise这一步是防止内部的循环引用。声明一个变量called相当于加了一把锁让promise只能调用一次成功或者失败回调防止死循环。解析x如果它的类型是object并且不为null或者它是一个函数并且它有then方法我们认为这是一个promise递归解析then里面再次调用resolvePromise
手写最后
因为promise在EventLoop里面是个微任务不过我们可以简单通过setTimout模拟。
然后我们再加上一些报错的捕获代码以及一些参数的兼容代码以及实现catch方法。
class Promise {constructor(executor) {// ...// 这里增加try catchtry {executor(this.resolve, this.reject)} catch (e) {reject(e)}}then(onFulfilled, onRejected) {// 这里兼容下 onFulfilled 和 onRejected 的传参onFulfilled typeof onFulfilled function ? onFulfilled : v vonRejected typeof onRejected function ? onRejected : err {throw err}const promise2 new Promise((resolve, reject) {if (this.status FULFILLED) {// 用 setTimeout 模拟异步setTimeout(() {try {const x onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)}if (this.status REJECTED) {// 用 setTimeout 模拟异步setTimeout(() {try {const x onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)}if (this.status PENDING) {this.onResolvedCallbacks.push(() {// 用 setTimeout 模拟异步setTimeout(() {try {const x onFulfilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)})this.onRejectedCallbacks.push(() {// 用 setTimeout 模拟异步setTimeout(() {try {const x onRejected(this.reason)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)})}})return promise2}// catch函数实际上里面就是调用了then方法catch (errCallback) {return this.then(null, errCallback)}
}
executor执行时增加try catch防止执行用户传入的函数直接就报错了这时我们应该直接rejectpromise。调用onFulfilled和onRejected时需要包裹setTimeout。 ok这样就大功告成了。最后我们来测试下我们写的promise是否符合规范。catch函数实际上里面就是调用了then方法然后第一个参数传null。
测试promise
promise是有规范的即Promises/A我们可以跑一段脚本测试写的promise是否符合规范。
首先需要在我们的promise增加如下代码
// 测试脚本
Promise.defer Promise.deferred function () {let dfd {}dfd.promise new Promise((resolve, reject) {dfd.resolve resolvedfd.reject reject})return dfd
}然后安装promises-aplus-tests包比如用npm可以使用命令npm install -g promises-aplus-tests安装到全局然后使用命令promises-aplus-tests 文件名即可进行测试里面有872测试用例全部通过即可以认为这是一个标准的promise。 完美最后面试官向你伸出了大拇指 前端面试题库 面试必备 推荐★★★★★
地址前端面试题库