目录
- 前言
- 一,手写教学
- 1.1 基本结构
- 1.2 resolve与reject结构搭建
- 1.3 resolve与reject代码实现
- 1.4 throw抛出异常改变状态
- 1.5 promise对象状态只能转换一次
- 1.6 then方法进行回调
- 1.7 异步任务的回调执行
- 1.8 执行多个回调的实现
- 1.9 同步修改状态then方法结果返回
- 1.10 异步修改状态then方法结果返回
- 二,总体代码
- 后记
前言
Promise是前端面试中笔记走重要的一个考点。如何真正的了解promise?我们不如自己动手,写一个可以拿来用的Promise.js。
下面是循序递进的写法教学,本文的最后也有完整写法的代码可以借鉴,希望能够帮助大家!
后面如果有机会会写一篇详解promise的博客,欢迎关注!
一,手写教学
1.1 基本结构
想要写一个最初的结构,我们不妨先用一下Promise,然后再进行分析。
let p = new Promise((resolve, reject) => {
resolve('巧克力小猫猿')
})
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
如上代码,我们了解到了,Promise构造函数里面传进去的是一个函数,所以我们也为其加一个函数类型的形参:
function Promise(executor) {
}
上述代码也用到了then方法,但目前我们的结构中还没有then,所以也写一个then,且有两个参数:
Promise.prototype.then = function(onResolved, onRejected) {
}
以上,基本结构已经写完。
1.2 resolve与reject结构搭建
我们都知道,在使用Promise的时候,内部会传一个执行器(函数),且该函数有两个函数类型的参数:resolve和reject。
因此,我们在写结构的时候也要声明着两个函数,并在内部执行器调用的时候使用它们,代码如下:
function Promise(executor) {
//同步调用
function resolve(data) {
}
function reject(data) {
}
executor(resolve, reject)
}
//先添加一个then
Promise.prototype.then = function(onResolved, onRejected) {
}
1.3 resolve与reject代码实现
我们先来复习一下,promise对象有两个属性,一个是promiseState,意为promise的状态,默认值为pedding,成功状态为resolved,失败状态为rejected;还有一个是promiseResult,意为promise返回的结果。这些变量既然是内置属性,我们应该先对其进行声明:
//声明属性,状态promiseState和结果值promiseResult
this.PromiseState = 'pedding'
this.promiseResult = null
接着,resolve与reject有两个作用:改变promise状态,返回结果。所以我们可以在resolve与reject两个函数中去操作promise的状态与结果:
//保存实例对象中的this
const self = this
function resolve(data) {
//修改状态(promiseState),设置对象结果值(promiseResult)
self.promiseState = 'resolved'
self.promiseResult = data
}
function reject(data) {
self.promiseState = 'rejected'
self.promiseResult = data
}
executor(resolve, reject)
这里为什么用的不是this是self?普通函数中的this都指向window,而这里我们修改的是构造函数中的属性,而不是window的。所以我们把构造函数中的this赋值给了self。注意,构造函数中的this指向的是构造函数。
1.4 throw抛出异常改变状态
在promise的使用中,一旦使用throw,则状态为失败,且返回值为throw中传入的。
那么,我们也要在构造函数内部写一个捕获异常的代码,利用try,catch。这里如果有疑问可以去搜索下try,catch的相关博客:
try{
executor(resolve, reject)
}catch(e) {
reject(e)
}
1.5 promise对象状态只能转换一次
在使用promise时,promise的状态只能转换一次:由pedding转换为resolved,或者由pedding转换为rejected。
所以本节我们来做这个限制:只需要在resolve与reject函数中加一段代码:
if (self.promiseState !== 'pedding') return
意思是:状态只能是由pedding改变为resolved与rejected的,不能由其他状态变为resolved与rejected。
1.6 then方法进行回调
首先我们来分析一下then的用法,看如下代码:
let p = new Promise((resolve, reject) => {
resolve('OK')
})
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
这一段代码,先使用resolve,并在里面传值,告诉promise,状态promiseState为resolved,传入的值promiseResult为ok。接着调用then,如果状态是成功的则调用第一个回调,失败则调用第二个。所以回调函数的调用需要判断条件,判断是成功还是失败,接着,value和reason是promiseResult,所以这两个回调的参数我们也要加上。
以上是思路,代码如下:
Promise.prototype.then = function (onResolved, onRejected) {
// 调用回调函数
if(this.promiseState === 'resolved') {
onResolved(this.promiseResult)
}
if(this.promiseState === 'rejected') {
onRejected(this.promiseResult)
}
}
1.7 异步任务的回调执行
如果没有书写相关代码,异步编程无法实现,如下代码:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resole('OK')
}, 1000)
})
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
由于这里有一个异步任务,所以最先执行的是p.then,而此时状态还未改变(pedding状态),所以无法输出,因此,我们需要做一些处理。
做什么处理呢:先思考,我们在什么时候使用then?两种情况,一种是上面同步的任务执行完毕,代码按顺序调用then;另一种就是,上面由异步任务,所以跳过先调用then。我们限制考虑的就是第二种情况,如果是这种情况的话,promise的状态还没有来的及改变(pedding),所以没办法执行代码。
我们希望的是在异步任务完成后再调用代码。那么如何实现这一点?如果调用then时状态为pedding,我们可以先声明一份当前的回调函数在当前的构造函数中:
//声明一个属性保存回调函数
this.callback = {}
接着,在then中对callback进行保存:
//判断pedding的状态
if (this.promiseState === 'pedding') {
//保存回调函数
this.callback = {
onResolved: onResolved,
onRejected: onRejected
}
}
在resolve函数中,等待状态改变后再调用then,也就是调用callback中对应的onResolved:
function resolve(data) {
//修改状态(promiseState),设置对象结果值(promiseResult)
//限制更改
if (self.promiseState !== 'pedding') return
self.promiseState = 'resolved'
self.promiseResult = data
//调用then
if (self.callback.onResolved) {
self.callback.onResolved(data)
}
}
同理,rejecte函数中也一样:
function reject(data) {
if (self.promiseState !== 'pedding') return
self.promiseState = 'rejected'
self.promiseResult = data
//调用then
if (self.callback.onRejected) {
self.callback.onRejected(data)
}
}
1.8 执行多个回调的实现
promise有一个特点,就是,promise可以串联多个操作任务。promise的then会返回一个新的promise,也就是then的链式调用。通过then的链式调用串联多个同步的异步任务。
目前我们的Promise没办法实现该功能,如果写多个then,前面的只会被覆盖。所以本节要解决的就是这个问题。
解决方法:把callback设置成一个数组,一旦是链式调用则在数组里添加内容:
this.callbacks = []
if (this.promiseState === 'pedding') {
//保存回调函数
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
调用方式也需要更改:
self.callbacks.forEach(item => {
item.onResolved(data)
})
self.callbacks.forEach(item => {
item.onRejected()
})
1.9 同步修改状态then方法结果返回
首先我们来回顾一下then方法的返回规律:then方法结果的返回在于回调函数的返回值,如果返回一个非promise的值,则结果是一个字符串;如果是一个promise类型的值,返回的是该promise的结果与状态。
这里我们做一步操作:then返回的是一个promise对象,接我们对该对象的返回值类型进行判断,如果返回的是一个promise对象,则另该promise对象调用then,一直调用到结果不是一个promise对象为止;如果结果不是promise对象则令其调用resolve:
Promise.prototype.then = function (onResolved, onRejected) {
return new Promise((resolve, rejecte) => {
if (this.promiseState === 'resolved') {
let result = onResolved(this.promiseResult)
//如果result是一个promise的对象
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
}
//此处省略了下面的代码
如果要抛出异常,状态会改变为失败:
return new Promise((resolve, rejecte) => {
if (this.promiseState === 'resolved') {
try{
let result = onResolved(this.promiseResult)
//如果result是一个promise的对象
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
}catch(e) {
reject(e)
}
}
这一块大家理解可能会有点困难,其实本质上是一个递归,一直在调用,直到返回值不再是promise类型的为止。
1.10 异步修改状态then方法结果返回
如下代码,在使用promise时,参杂异步任务:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 1000)
})
const res = p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
console.log(res)
利用我们的的代码,还没能来得及实现状态的改变和返回值的改变:
本节将要解决的就是这个问题。我们的代码对于pedding的判断有一个分支:
//判断pedding的状态
if (this.promiseState === 'pedding') {
//保存回调函数
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
对于异步任务,如果遇到先调用then的情况,1.7中给过解释:等待状态改变后立刻调用callback内部的函数。
//判断pedding的状态
if (this.promiseState === 'pedding') {
//保存回调函数
this.callbacks.push({
onResolved: function() {
onResolved(self.PromiseResult)
},
onRejected: function() {
onRejected(self.PromiseResult)
}
})
}
if (this.promiseState === 'pedding') {
//保存回调函数
this.callbacks.push({
onResolved: function() {
let result = onResolved(self.PromiseResult)
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
},
二,总体代码
以上,把Promise基本上可以实现。还有一些拓展的一些方法还未补充。有兴趣的读者朋友可以自己下来研究。
以下是整体代码:
function Promise(executor) {
//声明属性,状态promiseState和结果值promiseResult
this.promiseState = 'pedding'
this.promiseResult = null
//声明一个属性保存回调函数
this.callbacks = []
//保存实例对象中的this
const self = this
function resolve(data) {
//修改状态(promiseState),设置对象结果值(promiseResult)
//限制更改
if (self.promiseState !== 'pedding') return
self.promiseState = 'resolved'
self.promiseResult = data
//调用then
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
function reject(data) {
if (self.promiseState !== 'pedding') return
self.promiseState = 'rejected'
self.promiseResult = data
//调用then
self.callbacks.forEach(item => {
item.onRejected()
})
}
//异常处理
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
//先添加一个then
Promise.prototype.then = function (onResolved, onRejected) {
const self = this
// 调用回调函数
//返回值是一个promise对象
return new Promise((resolve, rejecte) => {
if (this.promiseState === 'resolved') {
try{
let result = onResolved(this.promiseResult)
//如果result是一个promise的对象
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
}catch(e) {
reject(e)
}
}
if (this.promiseState === 'rejected') {
try{
let result = onRejected(this.promiseResult)
//如果result是一个promise的对象
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
}catch(e) {
reject(e)
}
}
//判断pedding的状态
if (this.promiseState === 'pedding') {
//保存回调函数
this.callbacks.push({
onResolved: function() {
let result = onResolved(self.PromiseResult)
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
},
onRejected: function() {
let result = onRejected(self.PromiseResult)
if(result instanceof Promise) {
result.then(v => {
resolve(v);
}, r=> {
reject(r)
})
}else {
resolve(result)
}
}
})
}
})
}
后记
感谢阅读,后续有机会会出一篇关于Promise的博文,如果感兴趣可以先关注下,谢谢!