优雅,永不过时
尝试实现Promise类,并且追求代码的美感。
// promise对象的构造与使用
const promise = new Promise((resolve, reject) => {
resolve('promise成功了');
})
promise.then((value) => {
console.log(value)
})
看到promise对象的构造与使用,试想我们要写这样的一个类,大概要写些什么?粗劣一想,这两点必须有:
- 有一个构造函数,一个executor functon作为参数
- 有一个then方法,可以传递一到两个回调作为参数
这就来大概写一下:
class Promise {
constructor(executor){
this.state = 'pending'; // pending, fulfilled, rejected 三种状态
// 立即执行
executor(this._resolve, this._reject);
}
// #region 私有方法
_resolve = (value) => {
}
_reject = (reason) => {
}
// #endregion 私有方法
// #region 公有方法
then(successCallback, errorCallback) {
}
// #endregion 公有方法
}
优雅一下
- 私有方法已_开头,便于区分公私方法。
- 公私方法分堆摆放,用#region/#endregion这样的注释,这种注释vscode是支持将#region/#endregion中间的代码整体折叠的呦。
状态相关实现
一个 Promise 必然处于以下几种状态之一:待定(pending)、已兑现(fulfilled)、已拒绝(rejected)。异步操作最终得到成功返回值或者失败原因。我们实现一下这部分逻辑。
- 需要三个字段state(表示状态),value(成功的值),reasion(失败的原因)
- 需要两个字段successCallbacks(存储成功回调)、errorCallbacks(存储失败回调)
- _resolve方法做两件事,改变state状态为fulfilled和执行所有成功回调
- _reject方法做两件事,改变state状态为rejected和执行所有失败回调
class Promise {
constructor(executor){
this.state = 'pending'; // pending, fulfilled, rejected 三种状态
this.value = undefined;
this.reasion = undefined;
this.successCallbacks = []
this.errorCallbacks = []
// 立即执行
executor(this._resolve, this._reject);
}
// #region 私有方法
_resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.successCallbacks.forEach(callback => callback());
}
}
_reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.errorCallbacks.forEach(callback => callback());
}
}
// #endregion 私有方法
// #region 公有方法
then(successCallback, errorCallback) {
this.successCallbacks.push(successCallback)
this.errorCallbacks.push(errorCallback)
}
// #endregion 公有方法
}
优雅一下
我们对现有代码进行重构,让其更优雅一些:
class Promise {
// 构造器干两件事,初始化字段和执行executor。
constructor(executor){
// 1. 初始化字段
this._initProps();
// 2,执行executor
executor(this._resolve, this._reject);
}
// #region 私有方法
_initProps = () => {
this.state = 'pending'; // pending, fulfilled, rejected 三种状态
this.value = undefined;
this.reasion = undefined;
this.successCallbacks = []
this.errorCallbacks = []
}
_resolve = (value) => {
if(this.state !== 'pending') return; // 使用卫句,减少if嵌套
this.state = 'fulfilled';
this.value = value;
this.successCallbacks.forEach(callback => callback(value));
}
_reject = (reason) => {
if(this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
this.errorCallbacks.forEach(callback => callback(reason));
}
// #endregion 私有方法
// #region 公有方法
then(successCallback: value => value, errorCallback) {
this.successCallbacks.push(successCallback)
this.errorCallbacks.push(errorCallback)
}
// #endregion 公有方法
}
优雅一下
- 清晰的构造器逻辑,更容易让人理解你代码的逻辑。
- 使用卫句,可以减少嵌套,也更容易让人注意到方法的主体逻辑。
- 注意空行的使用,让逻辑关系紧密的代码在一块,增加代码可读性。
then方法实现
将之前写过都代码折叠,只看then方法。
class Promise {
// 构造器
...
// #region 私有方法
...
_thenWhenStateIsPending = (successCallback, errorCallback) => {
this.successCallbacks.push(() => { successCallback })
this.errorCallbacks.push(errorCallback)
}
_thenWhenStateIsFulfilled = (successCallback) => {
setTimeout(() => {
successCallback(this.value)
}, 0);
}
_thenWhenStateIsRejected = (errorCallback) => {
setTimeout(() => {
errorCallback(this.reason)
}, 0);
}
// #endregion 私有方法
// #region 公有方法
then(successCallback = (value => value), errorCallback = (err => { throw err })) {
switch(this.state) {
case 'pending':
this._thenWhenStateIsPending(successCallback, errorCallback);
break;
case 'fulfilled':
this._thenWhenStateIsFulfilled(successCallback);
break;
case 'rejected':
this._thenWhenStateIsRejected(errorCallback);
break;
}
}
// #endregion 公有方法
}
优雅一下
- 注意方法的单一职责,then方法是单一职责的,清晰都表达出会根据promise的不同状态做不同的处理。_thenWhenStateIsPending,_thenWhenStateIsFulfilled,_thenWhenStateIsRejected更是单一职责的,只处理一种状态下的then。
- 讲究都命名,处理then相关的私有方法,都叫then***,这增加了它们的相关性。
- 使用函数参数默认值,减少代码行数。
链式调用实现
then方法一定会返回一个新的promise对象
then方法一定会返回一个新的promise对象,所以我们这样写,声明一个promise变量,要求每个子then***方法到返回值都是promise对象,方法的最后把promise对象返回回去:
then(successCallback = (value => value), errorCallback = (err => { throw err })) {
let promise = null;
switch(this.state) {
case 'pending':
promise = this._thenWhenStateIsPending(successCallback, errorCallback);
break;
case 'fulfilled':
promise = this._thenWhenStateIsFulfilled(successCallback);
break;
case 'rejected':
promise = this._thenWhenStateIsRejected(errorCallback);
break;
}
return promise;
}
对于各个子then方法,这样写,每个then***方法都构造一个新的promise对象返回出去,另外定义一个_resolvePromise方法用来处理这个新创建的promise对象的状态:
_resolvePromise = (promise, resolve, reject, result) => {
// 处理then方法新创建的promise。
// 如果result是promise对象,则处理result对象
}
_thenWhenStateIsPending = (successCallback, errorCallback) => {
const promise = new Promise((resolve, reject) => {
this.successCallbacks.push(() => {
const result = successCallback(this.value)
this._resolvePromise(promise, resolve, reject, result)
})
this.errorCallbacks.push(() => {
const result = errorCallback(this.reason)
this._resolvePromise(promise, resolve, reject, result)
})
})
return promise
}
_thenWhenStateIsFulfilled = (successCallback) => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = successCallback(this.value)
this._resolvePromise(promise, resolve, reject, result)
}, 0)
})
return promise
}
_thenWhenStateIsRejected = (errorCallback) => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = errorCallback(this.reason)
this._resolvePromise(promise, resolve, reject, result)
}, 0)
})
return promise
}
实现_resolvePromise方法
_resolvePromise = (promise, resolve, reject, result) => {
if (result === promise) return reject(new Error('promise链死循环'))
if (result == null || result == undefined) return resolve(result)
if (typeof result !== 'object' && typeof result !== 'function') return resolve(result)
if (typeof result.then !== 'function') return resolve(result)
// 经历了一连串的卫句,剩下的result应该是个promise对象,或者说有then方法的对象
result.then(
(newResult) => {
this._resolvePromise(promise, resolve, reject, newResult)
},
(err) => {
reject(err)
}
)
}
新增公有方法catch、resolve、reject、race、all
// #region 公有方法
catch(errorCallback) {
this.then(null, errorCallback);
}
resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
all(promises) {
// 返回一个promise实例
return new Promise((resolve, reject) => {
// 做一个判断参数是否是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be Array'))
}
let count = 0
let newValues = new Array(promises.length) // 接收新的结果参数 建立一个伪数组
for (let i = 0; i < promises.length; i++) {
// 运用promise特性 只会有一个状态
Promise.resolve(promises[i]).then(
(res) => {
count++
newValues[i] = res // 把每次返回成功的数据添加到数组中
if (count === promises.length) {
// 数据接收完成
return resolve(newValues)
}
},
(rej) => reject(rej)
)
}
})
}
// #endregion 公有方法
Promise类全部代码
class Promise {
constructor(executor) {
this._initProps()
// 立即执行
try {
executor(this._resolve, this._reject)
} catch (e) {
this._reject(e)
}
}
// #region 私有方法
_initProps = () => {
this.state = 'pending' // pending, fulfilled, rejected 三种状态
this.value = undefined
this.reasion = undefined
this.successCallbacks = []
this.errorCallbacks = []
}
_resolve = (value) => {
if (this.state !== 'pending') return
this.state = 'fulfilled'
this.value = value
this.successCallbacks.forEach((callback) => callback())
}
_reject = (reason) => {
if (this.state !== 'pending') return
this.state = 'rejected'
this.reason = reason
this.errorCallbacks.forEach((callback) => callback())
}
_resolvePromise = (promise, resolve, reject, result) => {
if (result === promise) return reject(new Error('promise链死循环'))
if (result == null || result == undefined) return resolve(result)
if (typeof result !== 'object' && typeof result !== 'function') return resolve(result)
if (typeof result.then !== 'function') return resolve(result)
// 经历了一连串的卫句,剩下的result应该是个promise对象,或者说有then方法的对象
try{
result.then(
(newResult) => {
this._resolvePromise(promise, resolve, reject, newResult)
},
(err) => {
reject(err)
}
)
} catch (e){
reject(e)
}
}
_thenWhenStateIsPending = (successCallback, errorCallback) => {
const promise = new Promise((resolve, reject) => {
this.successCallbacks.push(() => {
const result = successCallback(this.value)
this._resolvePromise(promise, resolve, reject, result)
})
this.errorCallbacks.push(() => {
const result = errorCallback(this.reason)
this._resolvePromise(promise, resolve, reject, result)
})
})
return promise
}
_thenWhenStateIsFulfilled = (successCallback) => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = successCallback(this.value)
this._resolvePromise(promise, resolve, reject, result)
}, 0)
})
return promise
}
_thenWhenStateIsRejected = (errorCallback) => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = errorCallback(this.reason)
this._resolvePromise(promise, resolve, reject, result)
}, 0)
})
return promise
}
// #endregion 私有方法
// #region 公有方法
then(
successCallback = (value) => value,
errorCallback = (err) => {
throw err
}
) {
let promise = null
switch (this.state) {
case 'pending':
promise = this._thenWhenStateIsPending(successCallback, errorCallback)
break
case 'fulfilled':
promise = this._thenWhenStateIsFulfilled(successCallback)
break
case 'rejected':
promise = this._thenWhenStateIsRejected(errorCallback)
break
}
return promise
}
resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
all(promises) {
// 返回一个promise实例
return new Promise((resolve, reject) => {
// 做一个判断参数是否是数组
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be Array'))
}
let count = 0
let newValues = new Array(promises.length) // 接收新的结果参数 建立一个伪数组
for (let i = 0; i < promises.length; i++) {
// 运用promise特性 只会有一个状态
Promise.resolve(promises[i]).then(
(res) => {
count++
newValues[i] = res // 把每次返回成功的数据添加到数组中
if (count === promises.length) {
// 数据接收完成
return resolve(newValues)
}
},
(rej) => reject(rej)
)
}
})
}
// #endregion 公有方法
}
// 测试代码
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise
.then(() => {
console.log('then1')
})
.then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
})
.then(() => {
console.log('then2')
})
优雅一下
有没有感受到代码的整洁与漂亮呢,整体逻辑清晰,方法都是单一职责,容易阅读的。
小结
如果你阅读到最后,相信你一定感受到我标题所说的话,漂亮的代码会说话,通过阅读代码,就可以有效的理解它的意思。