
本文介绍了六、6. Promise 的封装求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。
对技术面试,学习经验等有一些体会,在此分享。
我们自己封装一个 Promise 对象有一定的难度。
但是只要我们抓住封装的核心思想:如何封装与如何使用息息相关,事情就会变得简单。
来分析一下:
以图片加载为例,Promise 的使用如下
function imageLoad = function(url) { const img = new Image() img.src = url return new Promise((resolve, reject) => { img.onload = function() { resolve('图片加载成功') } img.onerror = function() { resolve('图片加载失败') } }) }
封装好之后,我们就可以利用 imageLoad
来执行图片加载完成之后的逻辑
imageLoad('xxx.png').then(res => { console.log(res) // 此时的 res 为字符串:图片加载成功 }) .catch(err => { console.log(err) // 此时的 err 为字符串:图片加载失败 })
我们要抓住的核心关键是: Promise 最终的目的是,是为了执行 then 中的回调函数,我们给它取个名字为 then_cb
。
因此,我们就应该思考,如何在 Promise 对象内部,让 then_cb
执行。
显而易见,Promise 对象中,包含有原型方法 then,构造函数需要传入回调函数 executor
:该回调函数包含两个参数,resolve 与 reject。
因此我们可以依据这些已知点,得到如下结果:
class MyPromise { constroctor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) {} _reject(value) {} then(then_cb) {} }
我们目的是为了让 then_cb
执行。因此我们要回到 imageLoad
里, 去思考它的执行时机在哪里?
此时我们在 imageLoad
中发现,执行时机,只有 resolve
。
我们添加了一个 onload 事件的监听,当图片加载成功时,执行 resolve。
很显然,我们可以得出结论,then_cb
的执行需要被 resolve 触发。
但是目前来看,如果没有额外的手段,resolve 是无法执行 then_cb
的。要怎么办?
最简单的解决方案就是在 then 中抛出引用,在 resolve 中执行该引用。于是代码演变如下:
class MyPromise { constructor(executor) { this.thencallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { } then(then_cb) { this.thencallback = then_cb } }
同理,继续演变,解决 catch 的执行问题。
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { this.rejectcallback(value) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果不追求别的特性的话,我们的 Promise 对象就已经封装好,并且可以使用了。
写个例子简单验证一下
const p = new MyPromise((resolve, reject) => { setTimeout(() => { reject('异常信息') }, 1000) }) p.then(res => { console.log(res) })
完全符合预期。
然后简单调整,模拟将 then_cb 放入队列中执行,简单调整如下:
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果在面试中,被问到如何手写 Promise,实在想不起来,就把这段最简单的代码丢出去,基本上也能拿不错的高分啦。按照我的思路来想,十分简单。
将 Promise 的三种状态以及传递的值加入,让代码更规范一点
pending -> resolved
pending -> rejected
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' class MyPromise2 { constructor(executor) { this.thencallback = undefined this.rejectcallback = undefined this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return this._status = resolved this._value = value setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { if (!pending) return this._status = rejected this._value = rejected setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb console.log(then_cb) this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
then 可以多次调用,因此,缓存 then 的回调的引用,对应的应该是一个队列/数组。这样才能保证多次调用的 then_cb 都能执行。
因此,收集回调的 then
方法 与执行回调的 _resolve/_reject
都需要进行简单的调整
基于这个思路继续优化。
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' const isFunction = (fn) => typeof fn === 'function' class MyPromise2 { constructor(executor) { this.onResolvedQueue = [] this.onRejectedQueue = [] this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return const run = () => { this._status = resolved let cb; // 执行队列中收集的回调,执行一个,删除一个, // 队列思路:先进先出 while (cb = this.onResolvedQueue.shift()) { this._value = value cb(value) } } setTimeout(run, 0) } _reject(value) { if (!pending) return const run = () => { this._status = rejected this._value = value if (this.onRejectedQueue.length == 0) { throw new Error(value) } let cb while(cb = this.onRejectedQueue.shift()) { cb(value) } } setTimeout(run, 0) } then(onResolved, onRejected) { // 根据不同的状态执行不同的逻辑 if (this._status === pending) { if (isFunction(onResolved)) { this.onResolvedQueue.push(onResolved) } if (isFunction(onRejected)) { this.onRejectedQueue.push(onRejected) } } } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
验证一下,完美通过。
“`js
const p = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve(‘xxx’)
}, 1000)
})
p.then(res => {
console.log(res)
})
p.then(res => {
console.log(‘2 then’, res)
我们自己封装一个 Promise 对象有一定的难度。
但是只要我们抓住封装的核心思想:如何封装与如何使用息息相关,事情就会变得简单。
来分析一下:
以图片加载为例,Promise 的使用如下
function imageLoad = function(url) { const img = new Image() img.src = url return new Promise((resolve, reject) => { img.onload = function() { resolve('图片加载成功') } img.onerror = function() { resolve('图片加载失败') } }) }
封装好之后,我们就可以利用 imageLoad
来执行图片加载完成之后的逻辑
imageLoad('xxx.png').then(res => { console.log(res) // 此时的 res 为字符串:图片加载成功 }) .catch(err => { console.log(err) // 此时的 err 为字符串:图片加载失败 })
我们要抓住的核心关键是: Promise 最终的目的是,是为了执行 then 中的回调函数,我们给它取个名字为 then_cb
。
因此,我们就应该思考,如何在 Promise 对象内部,让 then_cb
执行。
显而易见,Promise 对象中,包含有原型方法 then,构造函数需要传入回调函数 executor
:该回调函数包含两个参数,resolve 与 reject。
因此我们可以依据这些已知点,得到如下结果:
class MyPromise { constroctor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) {} _reject(value) {} then(then_cb) {} }
我们目的是为了让 then_cb
执行。因此我们要回到 imageLoad
里, 去思考它的执行时机在哪里?
此时我们在 imageLoad
中发现,执行时机,只有 resolve
。
我们添加了一个 onload 事件的监听,当图片加载成功时,执行 resolve。
很显然,我们可以得出结论,then_cb
的执行需要被 resolve 触发。
但是目前来看,如果没有额外的手段,resolve 是无法执行 then_cb
的。要怎么办?
最简单的解决方案就是在 then 中抛出引用,在 resolve 中执行该引用。于是代码演变如下:
class MyPromise { constructor(executor) { this.thencallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { } then(then_cb) { this.thencallback = then_cb } }
同理,继续演变,解决 catch 的执行问题。
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { this.rejectcallback(value) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果不追求别的特性的话,我们的 Promise 对象就已经封装好,并且可以使用了。
写个例子简单验证一下
const p = new MyPromise((resolve, reject) => { setTimeout(() => { reject('异常信息') }, 1000) }) p.then(res => { console.log(res) })
完全符合预期。
然后简单调整,模拟将 then_cb 放入队列中执行,简单调整如下:
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果在面试中,被问到如何手写 Promise,实在想不起来,就把这段最简单的代码丢出去,基本上也能拿不错的高分啦。按照我的思路来想,十分简单。
将 Promise 的三种状态以及传递的值加入,让代码更规范一点
pending -> resolved
pending -> rejected
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' class MyPromise2 { constructor(executor) { this.thencallback = undefined this.rejectcallback = undefined this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return this._status = resolved this._value = value setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { if (!pending) return this._status = rejected this._value = rejected setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb console.log(then_cb) this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
then 可以多次调用,因此,缓存 then 的回调的引用,对应的应该是一个队列/数组。这样才能保证多次调用的 then_cb 都能执行。
因此,收集回调的 then
方法 与执行回调的 _resolve/_reject
都需要进行简单的调整
基于这个思路继续优化。
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' const isFunction = (fn) => typeof fn === 'function' class MyPromise2 { constructor(executor) { this.onResolvedQueue = [] this.onRejectedQueue = [] this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return const run = () => { this._status = resolved let cb; // 执行队列中收集的回调,执行一个,删除一个, // 队列思路:先进先出 while (cb = this.onResolvedQueue.shift()) { this._value = value cb(value) } } setTimeout(run, 0) } _reject(value) { if (!pending) return const run = () => { this._status = rejected this._value = value if (this.onRejectedQueue.length == 0) { throw new Error(value) } let cb while(cb = this.onRejectedQueue.shift()) { cb(value) } } setTimeout(run, 0) } then(onResolved, onRejected) { // 根据不同的状态执行不同的逻辑 if (this._status === pending) { if (isFunction(onResolved)) { this.onResolvedQueue.push(onResolved) } if (isFunction(onRejected)) { this.onRejectedQueue.push(onRejected) } } } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
验证一下,完美通过。
“`js
const p = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve(‘xxx’)
}, 1000)
})
p.then(res => {
console.log(res)
})
p.then(res => {
console.log(‘2 then’, res)
我们自己封装一个 Promise 对象有一定的难度。
但是只要我们抓住封装的核心思想:如何封装与如何使用息息相关,事情就会变得简单。
来分析一下:
以图片加载为例,Promise 的使用如下
function imageLoad = function(url) { const img = new Image() img.src = url return new Promise((resolve, reject) => { img.onload = function() { resolve('图片加载成功') } img.onerror = function() { resolve('图片加载失败') } }) }
封装好之后,我们就可以利用 imageLoad
来执行图片加载完成之后的逻辑
imageLoad('xxx.png').then(res => { console.log(res) // 此时的 res 为字符串:图片加载成功 }) .catch(err => { console.log(err) // 此时的 err 为字符串:图片加载失败 })
我们要抓住的核心关键是: Promise 最终的目的是,是为了执行 then 中的回调函数,我们给它取个名字为 then_cb
。
因此,我们就应该思考,如何在 Promise 对象内部,让 then_cb
执行。
显而易见,Promise 对象中,包含有原型方法 then,构造函数需要传入回调函数 executor
:该回调函数包含两个参数,resolve 与 reject。
因此我们可以依据这些已知点,得到如下结果:
class MyPromise { constroctor(executor) { executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) {} _reject(value) {} then(then_cb) {} }
我们目的是为了让 then_cb
执行。因此我们要回到 imageLoad
里, 去思考它的执行时机在哪里?
此时我们在 imageLoad
中发现,执行时机,只有 resolve
。
我们添加了一个 onload 事件的监听,当图片加载成功时,执行 resolve。
很显然,我们可以得出结论,then_cb
的执行需要被 resolve 触发。
但是目前来看,如果没有额外的手段,resolve 是无法执行 then_cb
的。要怎么办?
最简单的解决方案就是在 then 中抛出引用,在 resolve 中执行该引用。于是代码演变如下:
class MyPromise { constructor(executor) { this.thencallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { } then(then_cb) { this.thencallback = then_cb } }
同理,继续演变,解决 catch 的执行问题。
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { this.thencallback(value) } _reject(value) { this.rejectcallback(value) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果不追求别的特性的话,我们的 Promise 对象就已经封装好,并且可以使用了。
写个例子简单验证一下
const p = new MyPromise((resolve, reject) => { setTimeout(() => { reject('异常信息') }, 1000) }) p.then(res => { console.log(res) })
完全符合预期。
然后简单调整,模拟将 then_cb 放入队列中执行,简单调整如下:
class MyPromise { constructor(executor) { this.thencallback = null this.rejectcallback = null executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } }
如果在面试中,被问到如何手写 Promise,实在想不起来,就把这段最简单的代码丢出去,基本上也能拿不错的高分啦。按照我的思路来想,十分简单。
将 Promise 的三种状态以及传递的值加入,让代码更规范一点
pending -> resolved
pending -> rejected
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' class MyPromise2 { constructor(executor) { this.thencallback = undefined this.rejectcallback = undefined this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return this._status = resolved this._value = value setTimeout(() => { this.thencallback(value) }, 0) } _reject(value) { if (!pending) return this._status = rejected this._value = rejected setTimeout(() => { this.rejectcallback(value) }, 0) } then(then_cb, onRejected) { this.thencallback = then_cb console.log(then_cb) this.rejectcallback = onRejected } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
then 可以多次调用,因此,缓存 then 的回调的引用,对应的应该是一个队列/数组。这样才能保证多次调用的 then_cb 都能执行。
因此,收集回调的 then
方法 与执行回调的 _resolve/_reject
都需要进行简单的调整
基于这个思路继续优化。
const pending = 'PENDING' const resolved = 'RESOLVED' const rejected = 'REJECTED' const isFunction = (fn) => typeof fn === 'function' class MyPromise2 { constructor(executor) { this.onResolvedQueue = [] this.onRejectedQueue = [] this._value = undefined this._status = pending executor(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { if (!pending) return const run = () => { this._status = resolved let cb; // 执行队列中收集的回调,执行一个,删除一个, // 队列思路:先进先出 while (cb = this.onResolvedQueue.shift()) { this._value = value cb(value) } } setTimeout(run, 0) } _reject(value) { if (!pending) return const run = () => { this._status = rejected this._value = value if (this.onRejectedQueue.length == 0) { throw new Error(value) } let cb while(cb = this.onRejectedQueue.shift()) { cb(value) } } setTimeout(run, 0) } then(onResolved, onRejected) { // 根据不同的状态执行不同的逻辑 if (this._status === pending) { if (isFunction(onResolved)) { this.onResolvedQueue.push(onResolved) } if (isFunction(onRejected)) { this.onRejectedQueue.push(onRejected) } } } catch(onRejected) { this.then(null, onRejected) } } window.MyPromise2 = MyPromise2
验证一下,完美通过。
“`js
const p = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve(‘xxx’)
}, 1000)
})
p.then(res => {
console.log(res)
})
p.then(res => {
console.log(‘2 then’, res)
部分转自互联网,侵权删除联系
最新评论