前言
Promise 是异步编程的一种解决方案,比传统的解决方案------回调函数和事件------更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
Promise
对象。
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是"承诺",表示其他手段无法改变。
手写Promise
JavaScript 中,类是一个非常实用的一个方法,今天我们就使用它来简单封装一个 Promise。ES6 中类的使用这里就不多介绍了。
一、创建一个 PromiseA 的类。
首先我们需要创建这个类,如下:
kotlin
class PromiseA {
constructor(executor) {
this.value = null // 储存成功的值
this.reason = null // 储存失败的值
this.state = 'pending' // 储存状态值
const onResolve = (value) => {
if (this.state == 'pending') {
this.value = value // 改变成功的值
this.state = 'fulfilled' // 改变状态值
}
}
const onReject = (err) => {
if (this.state == 'pending') {
this.reason = err // 改变失败的值
this.state = 'rejected' // 改变状态值
}
}
executor(onResolve, onReject)
}
then(onResolve, onReject) {
if (this.state == 'fulfilled') {
onResolve(this.value) // 调用成功的函数
}
if (this.state == 'rejected') {
onReject(this.reason) // 调用失败的函数
}
}
}
以上代码中,我们简单的创建了一个 PromiseA 的类,其中 onResolve
是成功的函数,onReject
是失败的函数。value、reason 分别是成功和失败的值。state
则是 Promise 的状态值。
两个函数中之所以需要判断 state
的值是因为 Promise 的特性:状态凝固,之前的文章中有提到过。
二、处理 Promise 的三种状态。
上面的代码测试的时候会发现,同步的简单处理是没有问题的,但它主要的功能"处理异步"是无法使用的,接下来我们需要处理一下 pending
(进行中)状态的问题。代码如下:
kotlin
class PromiseA {
constructor(executor) {
this.value = null
this.reason = null
this.state = 'pending'
this.onResolveBack = [] // 储存成功的函数
this.onRejectBack = [] // 储存失败的函数
const onResolve = (value) => {
if (this.state == 'pending') {
this.value = value
this.state = 'fulfilled'
this.onResolveBack.forEach(fn => fn()) // 调用成功的函数
}
}
const onReject = (err) => {
if (this.state == 'pending') {
this.reason = err
this.state = 'rejected'
this.onRejectBack.forEach(fn => fn()) // 调用失败的函数
}
}
executor(onResolve, onReject)
}
then(onResolve, onReject) {
if (this.state == 'fulfilled') {
onResolve(this.value)
}
if (this.state == 'rejected') {
onReject(this.reason)
}
if (this.state == 'pending') {
// 储存成功的函数
this.onResolveBack.push(() => {
onResolve(this.value)
})
// 储存失败的函数
this.onRejectBack.push(() => {
onReject(this.reason)
})
}
}
}
以上代码中,我们又添加了两个数组用来存储成功或者失败的函数。需要注意的是,我们存储的时候需要在外包一层函数,用来传递我们的成功或者失败的值。
如果是异步的话,PromiseA 的状态是进行中的,就直接存储到我们的数组中,等待异步执行的时候,使用 forEach 方法循环执行我们的函数。
三、解决链式调用和处理循环调用
以上,我们基本完成了 Promise 的手写,接下来需要处理一下链式调用的问题。代码如下:(由于需要更改的代码是在 then 内的,所以其他的这里就不再添加了)
typescript
then(onResolve, onReject) {
// Promise1 是链式后的 Promise
const Promise1 = new PromiseA((resolve, reject) => {
if (this.state == 'fulfilled') {
setTimeout(() => {
resolvePromise(onResolve(this.value), resolve, reject, Promise1)
})
}
if (this.state == 'rejected') {
setTimeout(() => {
resolvePromise(onReject(this.reason), resolve, reject, Promise1)
})
}
if (this.state == 'pending') {
this.onResolveBack.push(() => {
setTimeout(() => {
resolvePromise(onResolve(this.value), resolve, reject, Promise1)
})
})
this.onRejectBack.push(() => {
setTimeout(() => {
resolvePromise(onReject(this.reason), resolve, reject, Promise1)
})
})
}
})
return Promise1
}
function resolvePromise(type, resolve, reject, Promise1) {
// 处理循环调用问题
if (type === Promise1) {
const err = `Uncaught(in promise) YypeError: Chaining cycle deteted for promise`
console.error(err);
return reject(new TypeError(err))
}
// 判断是否是一个Promise
if (type instanceof PromiseA) {
type.then(resolve, reject)
} else {
resolve(type)
}
}
以上代码中,我们把 then 方法中的内容使用 PromiseA 包裹了起来,并 return 出去。其中,我们还封装了一个方法用来判断前一个 then 方法的返回值,如果是普通值,直接调用。如果是 Promise 对象,看是否成功,如果成功 resolve ,如果失败 reject 并将值返回。
到这里,我们手写的简易 Promise 就已经完成了。
四、Promise 的四个静态方法
首先是两个最简单的 resolve、reject 方法,如下:
javascript
PromiseA.resolve = value => {
return new PromiseA((resolve, reject) => {
resolve(value)
})
}
PromiseA.reject = value => {
return new PromiseA((resolve, reject) => {
reject(value)
})
}
race 方法:
javascript
PromiseA.race = (promises) => {
return new PromiseA((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
all 方法:
scss
PromiseA.all = (promises) => {
const arr = []; //所有数据存储的地方
var i = 0; //数据累计的地方
function proscessDate(index, data, resolve) {
// 每个数据都能按照位置添加进去
arr[index] = data;
// 统计当前加载了几个
i++;
// 判断当前是否已经加载完成,加载完成使用resolve 整体将数据返回出去
if (i == promises.length) {
resolve(arr);
}
}
return new PromiseA((resolve, reject) => {
for (let j = 0; j < promises.length; j++) {
promises[j].then((value) => {
proscessDate(j, value, resolve)
}, (err) => {
reject(err)
})
}
})
}
Promise 的四个静态方法具体的使用,请参考《阮一峰 ECMAScript 6 (ES6) 标准入门教程 第三版》
今天的分享就到这里了,感谢您的观看!