Promise 是ES6异步编程的一种解决方案:
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:
pending(等待),
fulfiled(成功),
rejected(失败) ;
状态一旦改变,就不会再变。
创造promise实例后,它会立即执行。
让我们一起实现一款自己的Promise,顺便还可以理解理解源码。
Let’s go
首先打开我们的编辑器,新建一个.js格式的文件。声明Padding、Fulfied、Rejected三种状态,然后创建一个Class,当然你也可以使用构造函数写。
为了方便理解,每行代码都有注释。
//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝
//创建一个名为 MyPromise 的类
class MyPromise {
//写个构造器方便接受 new 的时候传进来的参数
// 构造器
constructor(executor) {
// 成功回调的队列
this.resolveQueue = []
// 拒绝/失败的回调队列
this.rejectQueue = []
// 储存当前队列的value
this.currentValue = null
}
}
// 构造器
constructor(executor) {
// 成功回调的队列
this.resolveQueue = []
// 拒绝/失败的回调队列
this.rejectQueue = []
// 储存当前队列的value
this.currentValue = null
// 判断executor是否是一个函数
if (typeof executor !== 'function') {
throw new Error('MyPromise is not a function')
}
// 修改状态为Padding
this.status = Padding
// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
let _resolve = value => {
/*
把resolve执行回调的操作封装成一个函数,
放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
我们还可以使用MutationObserver
*/
let run = () => {
// 判断并修改状态
if (this.modifyStatus(Fulfilled)) return
// 储存当前的value
this.currentValue = value
// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空
/*
这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>
如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调
*/
while (this.resolveQueue.length) {
// 弹出
let callback = this.resolveQueue.shift()
// 执行
callback(value)
}
}
// 调用
setTimeout(run);
}
// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
let _reject = value => {
/*
把reject执行回调的操作封装成一个函数,
放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
我们还可以使用MutationObserver
*/
let run = () => {
// 判断并修改状态
if (this.modifyStatus(Rejected)) return
// 储存当前的value
this.currentValue = value
// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空
/*
这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>
如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调
*/
while (this.rejectQueue.length) {
// 弹出
let callback = this.rejectQueue.shift()
// 执行
callback(value)
}
}
// 调用
setTimeout(run)
}
// 执行
executor(_resolve, _reject)
}
这个是promise的重要点,除了finally、reject不需要,其他的都需要用到他的原理内容。
// resolve/then/成功
then(resolvedFn, rejectedFn) {
/*
根据Promise A+规范,如果 then() 接收的参数不是function,那么我们应该忽略它。
如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
*/
// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒
if (resolvedFn && typeof resolvedFn !== 'function') {
throw new Error('resolver is not a function')
}
// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒
if (rejectedFn && typeof rejectedFn !== 'function') {
throw new Error('rejecter is not a function')
}
// 为了实现.then的链式调用,此时需要返回一个promise
return new MyPromise((resolve, reject) => {
/*
then
*/
//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
let fulfilledFn = value => {
try {
//执行第一个(当前的)Promise的成功回调,并获取返回值
let x = resolvedFn(value)
//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 设置对列
this.resolveQueue.push(fulfilledFn)
/*
reject
根据设置resolvedFn的原理,设置一下rejectFn
*/
//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
let rejectFn = value => {
try {
//执行第一个(当前的)Promise的成功回调,并获取返回值
let x = rejectedFn(value)
//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 设置对列
this.rejectQueue.push(rejectFn)
/*
处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,
有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),
如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,
因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调
*/
// 当前状态判断
switch (this._status) {
// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行
case Padding:
this.resolveQueue.push(fulfilledFn)
this.rejectQueue.push(rejectFn)
break;
// 当状态已经变为resolve/reject时,直接执行then回调
case Fulfilled:
fulfilledFn(this.currentValue)
break;
case Rejected:
rejectFn(this.currentValue)
break;
}
})
}
// reject/catch 失败
catch (rejectedFn) {
// 判断是否是一个函数
if (typeof rejectedFn === 'function') {
return this.then(null, rejectedFn)
}
}
// resolve
/*
resolve(resolver)方法返回一个以给定值解析后的Promise 对象。
如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
*/
static resolve(resolver) {
// 根据规范, 如果参数是Promise实例, 直接return这个实例
if (resolver instanceof MyPromise) return resolver
return new MyPromise(resolve => resolve(resolver))
}
// reject
/* reject()方法返回一个带有拒绝原因的Promise对象。 */
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
finally 无论成功失败都会返回,该方法与状态无关
// finally 无论成功失败都会返回-该方法与状态无关
finally(finallyFn) {
return this.then(
// resolve执行回调,并在then中return结果传递给后面的Promise
value => MyPromise.resolve(callback()).then(() => value),
// resolve执行回调,并在then中return结果传递给后面的Promise
reason => MyPromise.resolve(callback()).then(() => {
throw reason
})
)
}
Promise.all方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成**(resolved)**
或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),
此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
/*
Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),
此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
*/
static all(promiseArr) {
let index = 0
let result = []
return new MyPromise((resolve, reject) => {
promiseArr.forEach((item, itemIndex) => {
//resolve(item)用于处理传入的值不为Promise的情况
MyPromise.resolve(item).then(
val => {
index++
result[itemIndex] = value
//所有then执行后, resolve结果
if (index === promiseArr.length) {
resolve(result)
}
}
,
//有一个Promise被reject时,MyPromise的状态变为reject
err => reject(err)
)
})
})
}
race方法返回一个 promise,
一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。
/*
race(iterable)方法返回一个 promise,
一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。
*/
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
for (let item of promiseArr) {
MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况
value => resolve(value),
err => reject(err)
)
}
})
}
我这里是把修改状态单独拿出来的,代码如下:
// 修改状态
modifyStatus(status) {
/*
因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去
这里return 为true是因为上面通过这个做过状态校验判断
*/
if (this.status !== Padding) return true
// 修改状态
this.status = status
}
不想复制阅读的同学可以点击 -> Promise 原理/实现自己的Promise
进行下载。使用的时候就像我们日常使用Promise那样使用就OK了,只是我们需要导入这个js
//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝
// 声明类
class MyPromise {
// 构造器
constructor(executor) {
// 成功回调的队列
this.resolveQueue = []
// 拒绝/失败的回调队列
this.rejectQueue = []
// 储存当前队列的value
this.currentValue = null
// 判断executor是否是一个函数
if (typeof executor !== 'function') {
throw new Error('MyPromise is not a function')
}
// 修改状态为Padding
this.status = Padding
// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
let _resolve = value => {
/*
把resolve执行回调的操作封装成一个函数,
放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
我们还可以使用MutationObserver
*/
let run = () => {
// 判断并修改状态
if (this.modifyStatus(Fulfilled)) return
// 储存当前的value
this.currentValue = value
// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空
/*
这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>
如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调
*/
while (this.resolveQueue.length) {
// 弹出
let callback = this.resolveQueue.shift()
// 执行
callback(value)
}
}
// 调用
setTimeout(run);
}
// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
let _reject = value => {
/*
把reject执行回调的操作封装成一个函数,
放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
我们还可以使用MutationObserver
*/
let run = () => {
// 判断并修改状态
if (this.modifyStatus(Rejected)) return
// 储存当前的value
this.currentValue = value
// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空
/*
这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>
如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调
*/
while (this.rejectQueue.length) {
// 弹出
let callback = this.rejectQueue.shift()
// 执行
callback(value)
}
}
// 调用
setTimeout(run)
}
// 执行
executor(_resolve, _reject)
}
// resolve/then/成功
then(resolvedFn, rejectedFn) {
/*
根据规范,如果 then() 接收的参数不是function,那么我们应该忽略它。
如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
*/
// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒
if (resolvedFn && typeof resolvedFn !== 'function') {
throw new Error('resolver is not a function')
}
// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒
if (rejectedFn && typeof rejectedFn !== 'function') {
throw new Error('rejecter is not a function')
}
// 为了实现.then的链式调用,此时需要返回一个promise
return new MyPromise((resolve, reject) => {
/*
then
*/
//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
let fulfilledFn = value => {
try {
//执行第一个(当前的)Promise的成功回调,并获取返回值
let x = resolvedFn(value)
//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 设置对列
this.resolveQueue.push(fulfilledFn)
/*
reject
根据设置resolvedFn的原理,设置一下rejectFn
*/
//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
let rejectFn = value => {
try {
//执行第一个(当前的)Promise的成功回调,并获取返回值
let x = rejectedFn(value)
//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 设置对列
this.rejectQueue.push(rejectFn)
/*
处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,
有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),
如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,
因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调
*/
// 当前状态判断
switch (this._status) {
// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行
case Padding:
this.resolveQueue.push(fulfilledFn)
this.rejectQueue.push(rejectFn)
break;
// 当状态已经变为resolve/reject时,直接执行then回调
case Fulfilled:
fulfilledFn(this.currentValue)
break;
case Rejected:
rejectFn(this.currentValue)
break;
}
})
}
// reject/catch 失败
catch (rejectedFn) {
// 判断是否是一个函数
if (typeof rejectedFn === 'function') {
return this.then(null, rejectedFn)
}
}
// finally 无论成功失败都会返回-该方法与状态无关
finally(finallyFn) {
return this.then(
// resolve执行回调,并在then中return结果传递给后面的Promise
value => MyPromise.resolve(callback()).then(() => value),
// resolve执行回调,并在then中return结果传递给后面的Promise
reason => MyPromise.resolve(callback()).then(() => {
throw reason
})
)
}
// resolve
/*
resolve(resolver)方法返回一个以给定值解析后的Promise 对象。
如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
*/
static resolve(resolver) {
// 根据规范, 如果参数是Promise实例, 直接return这个实例
if (resolver instanceof MyPromise) return resolver
return new MyPromise(resolve => resolve(resolver))
}
// reject
/* reject()方法返回一个带有拒绝原因的Promise对象。 */
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
// all
/*
Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),
此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
*/
static all(promiseArr) {
let index = 0
let result = []
return new MyPromise((resolve, reject) => {
promiseArr.forEach((item, itemIndex) => {
//resolve(item)用于处理传入的值不为Promise的情况
MyPromise.resolve(item).then(
val => {
index++
result[itemIndex] = value
//所有then执行后, resolve结果
if (index === promiseArr.length) {
resolve(result)
}
}
,
//有一个Promise被reject时,MyPromise的状态变为reject
err => reject(err)
)
})
})
}
// race
/*
race(iterable)方法返回一个 promise,
一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。
*/
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
for (let item of promiseArr) {
MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况
value => resolve(value),
err => reject(err)
)
}
})
}
// 修改状态
modifyStatus(status) {
/*
因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去
这里return 为true是因为上面通过这个做过状态校验判断
*/
if (this.status !== Padding) return true
// 修改状态
this.status = status
}
}