天天看點

Promise源碼實作

ECMAscript 6 原生提供了 Promise 對象,用來優雅解決異步問題,避免回調噩夢。

Promise對象有三種狀态:

  • pending: 初始狀态,不是成功或失敗狀态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失敗。

實作Promise:

目标:可以實作優雅的異步處理方式,以下是想要達到最終狀态:

new MyPromise(function(resolve){
	setTimeout(()=>{
		resolve(122121)
	},1000)
}).then(r=>{
	console.log(r)
}).catch(e=>{
	console.log(e)
})
           

第一步:定義狀态,初始化Promise外殼

  • 定義三種狀态常量、增加判斷是否為函數方法(Promise參數必須是函數)
  • 初始化狀态、傳回值、成功或失敗隊列
  • 初始化then、catch、resolve、reject、all、race、finally方法(簡易Promise隻需then和catch即可)
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
	constructor (handle) {
		if (!isFunction(handle)) {
			throw new Error('MyPromise must accept a function as a parameter')
		}
		// 初始化狀态
		this._status = PENDING
        // 初始化傳回值
        this._value = undefined
		// 初始化成功回調函數隊列
		this._fulfilledQueues = []
		// 初始化失敗回調函數隊列
		this._rejectedQueues = []
        // todo
	}
	then (onFulfilled, onRejected) {
		// todo
	}
	// 添加catch方法
	catch (onRejected) {
		// todo
	}
	// 添加靜态resolve方法
	static resolve (value) {
		// todo
	}
	// 添加靜态reject方法
	static reject (value) {
		// todo
	}
	// 添加靜态all方法
	static all (list) {
		// todo
	}
	// 添加靜态race方法
	static race (list) {
		// todo
	}
	finally (cb) {
		// todo
	}
}
           

第二步:實作傳遞參數resolve和reject

  • 執行回調函數handle方法,并傳入resolve和reject方法,友善回調函數傳值
  • 了解resolve、reject方法,在傳入的函數中,resolve可以把promise的狀态改為FULFILLED,reject改為REJECTED
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
	constructor (handle) {
		if (!isFunction(handle)) {
			throw new Error('MyPromise must accept a function as a parameter')
		}
		// 初始化狀态
		this._status = PENDING
        // 初始化傳回值
        this._value = undefined
		// 初始化成功回調函數隊列
		this._fulfilledQueues = []
		// 初始化失敗回調函數隊列
		this._rejectedQueues = []
        // 執行回調函數handle
        try {
			handle(this._resolve.bind(this), this._reject.bind(this)) 
		} catch (err) {
			this._reject(err)
		}
	}
}
           

resolve函數待辦項

  • 修改PENDING狀态為FULFILLED狀态
  • 執行成功隊列的函數
  • 指派this._value -- resolve函數傳遞的值
_resolve (val) {
		if (this._status !== PENDING) return
		this._status = FULFILLED
		// 依次執行成功隊列中的函數,并清空隊列
		const runFulfilled = (value) => {
			let cb;
			while (cb = this._fulfilledQueues.shift()) {
				cb(value)
			}
		}
		this._value = val
		runFulfilled(val)
	}
           

resolve函數優化:

  • 支援同步Promise
  • 支援鍊式調用
_resolve (val) {
    	const run = () => {
			if (this._status !== PENDING) return
			this._status = FULFILLED
			// 依次執行成功隊列中的函數,并清空隊列
			const runFulfilled = (value) => {
				let cb;
				while (cb = this._fulfilledQueues.shift()) {
					cb(value)
				}
			}
			// 依次執行失敗隊列中的函數,并清空隊列
			const runRejected = (error) => {
				let cb;
				while (cb = this._rejectedQueues.shift()) {
					cb(error)
				}
			}
			/* 如果resolve的參數為Promise對象,則必須等待該Promise對象狀态改變後,
				目前Promsie的狀态才會改變,且狀态取決于參數Promsie對象的狀态
			*/
			if (val instanceof MyPromise) {
				val.then(value => {
					this._value = value
					runFulfilled(value)
				}, err => {
					this._value = err
					runRejected(err)
				})
			} else {
				this._value = val
				runFulfilled(val)
			}
		}
		// 為了支援同步的Promise,這裡采用異步調用
		setTimeout(run, 0)
	}
           

reject函數待辦項

  • 修改PENDING狀态為REJECTED狀态
  • 執行失敗隊列的函數
  • 指派this._value -- reject函數傳遞的值
_reject (err) { 
		if (this._status !== PENDING) return
		// 依次執行失敗隊列中的函數,并清空隊列
		const run = () => {
			this._status = REJECTED
			this._value = err
			let cb;
			while (cb = this._rejectedQueues.shift()) {
				cb(err)
			}
		}
		// 為了支援同步的Promise,這裡采用異步調用
		setTimeout(run, 0)
	}
           

then函數待辦項

  • 當狀态為pengding時,将then方法加入執行隊列等待執行
  • 當狀态已經改變時,立即執行對應的回調函數
then (onFulfilled, onRejected) {
	const { _value, _status } = this
	// 傳回一個新的Promise對象
	return new MyPromise((onFulfilledNext, onRejectedNext) => {
		// 封裝一個成功時執行的函數
		let fulfilled = value => {
			try {
				if (!isFunction(onFulfilled)) {
					onFulfilledNext(value)
				} else {
					let res =  onFulfilled(value);
					if (res instanceof MyPromise) {
						// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
						res.then(onFulfilledNext, onRejectedNext)
					} else {
						//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
						onFulfilledNext(res)
					}
				}
			} catch (err) {
				// 如果函數執行出錯,新的Promise對象的狀态為失敗
				onRejectedNext(err)
			}
		}
		// 封裝一個失敗時執行的函數
		let rejected = error => {
			try {
				if (!isFunction(onRejected)) {
					onRejectedNext(error)
				} else {
						let res = onRejected(error);
						if (res instanceof MyPromise) {
							// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
							res.then(onFulfilledNext, onRejectedNext)
						} else {
							//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
							onFulfilledNext(res)
						}
				}
			} catch (err) {
				// 如果函數執行出錯,新的Promise對象的狀态為失敗
				onRejectedNext(err)
			}
		}
		switch (_status) {
			// 當狀态為pending時,将then方法回調函數加入執行隊列等待執行
			case PENDING:
				this._fulfilledQueues.push(fulfilled)
				this._rejectedQueues.push(rejected)
				break
			// 當狀态已經改變時,立即執行對應的回調函數
			case FULFILLED:
				fulfilled(_value)
				break
			case REJECTED:
				rejected(_value)
				break
		}
	})
}
           

其他函數實作起來比較簡單,其實就是各種文法糖。

catch函數:

  • 直接指向then的第二個方法
catch (onRejected) {
	return this.then(undefined, onRejected)
}
           

resolve函數 

  • new Promise((resolve)=>resolve())的文法糖
static resolve (value) {
	// 如果參數是MyPromise執行個體,直接傳回這個執行個體
	if (value instanceof MyPromise) return value
	return new MyPromise(resolve => resolve(value))
}
           

reject函數

  • new Promise((resolve, reject)=>reject())的文法糖
static reject (value) {
	return new MyPromise((resolve ,reject) => reject(value))
}
           

all函數

  • 所有Promise都完成執行函數
static all (list) {
		return new MyPromise((resolve, reject) => {
			/**
			 * 傳回值的集合
			 */
			let values = []
			let count = 0
			for (let [i, p] of list.entries()) {
				// 數組參數如果不是MyPromise執行個體,先調用MyPromise.resolve
				this.resolve(p).then(res => {
					values[i] = res
					count++
					// 所有狀态都變成fulfilled時傳回的MyPromise狀态就變成fulfilled
					if (count === list.length) resolve(values)
				}, err => {
					// 有一個被rejected時傳回的MyPromise狀态就變成rejected
					reject(err)
				})
			}
		})
	}
           

race函數

  • 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
static race (list) {
		return new MyPromise((resolve, reject) => {
			for (let p of list) {
				// 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
				this.resolve(p).then(res => {
					resolve(res)
				}, err => {
					reject(err)
				})
			}
		})
	}
           

finally函數

  • 無論成功或失敗都會執行的函數 
finally (cb) {
	return this.then(
		value  => MyPromise.resolve(cb()).then(() => value),
		reason => MyPromise.resolve(cb()).then(() => { throw reason })
	);
}
           

完整源碼:

// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
	constructor (handle) {
		if (!isFunction(handle)) {
			throw new Error('MyPromise must accept a function as a parameter')
		}
		// 添加狀态
		this._status = PENDING
		// 添加狀态
		this._value = undefined
		// 添加成功回調函數隊列
		this._fulfilledQueues = []
		// 添加失敗回調函數隊列
		this._rejectedQueues = []
		// 執行handle
		try {
			handle(this._resolve.bind(this), this._reject.bind(this)) 
		} catch (err) {
			this._reject(err)
		}
	}
	// 添加resovle時執行的函數
	_resolve (val) {
		const run = () => {
			if (this._status !== PENDING) return
			this._status = FULFILLED
			// 依次執行成功隊列中的函數,并清空隊列
			const runFulfilled = (value) => {
				let cb;
				while (cb = this._fulfilledQueues.shift()) {
					cb(value)
				}
			}
			// 依次執行失敗隊列中的函數,并清空隊列
			const runRejected = (error) => {
				let cb;
				while (cb = this._rejectedQueues.shift()) {
					cb(error)
				}
			}
			/* 如果resolve的參數為Promise對象,則必須等待該Promise對象狀态改變後,
				目前Promsie的狀态才會改變,且狀态取決于參數Promsie對象的狀态
			*/
			if (val instanceof MyPromise) {
				val.then(value => {
					this._value = value
					runFulfilled(value)
				}, err => {
					this._value = err
					runRejected(err)
				})
			} else {
				this._value = val
				runFulfilled(val)
			}
		}
		// 為了支援同步的Promise,這裡采用異步調用
		setTimeout(run, 0)
	}
	// 添加reject時執行的函數
	_reject (err) { 
		if (this._status !== PENDING) return
		// 依次執行失敗隊列中的函數,并清空隊列
		const run = () => {
			this._status = REJECTED
			this._value = err
			let cb;
			while (cb = this._rejectedQueues.shift()) {
				cb(err)
			}
		}
		// 為了支援同步的Promise,這裡采用異步調用
		setTimeout(run, 0)
	}
	// 添加then方法
	then (onFulfilled, onRejected) {
		const { _value, _status } = this
		// 傳回一個新的Promise對象
		return new MyPromise((onFulfilledNext, onRejectedNext) => {
			// 封裝一個成功時執行的函數
			let fulfilled = value => {
				try {
					if (!isFunction(onFulfilled)) {
						onFulfilledNext(value)
					} else {
						let res =  onFulfilled(value);
						if (res instanceof MyPromise) {
							// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
							res.then(onFulfilledNext, onRejectedNext)
						} else {
							//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
							onFulfilledNext(res)
						}
					}
				} catch (err) {
					// 如果函數執行出錯,新的Promise對象的狀态為失敗
					onRejectedNext(err)
				}
			}
			// 封裝一個失敗時執行的函數
			let rejected = error => {
				try {
					if (!isFunction(onRejected)) {
						onRejectedNext(error)
					} else {
							let res = onRejected(error);
							if (res instanceof MyPromise) {
								// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
								res.then(onFulfilledNext, onRejectedNext)
							} else {
								//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
								onFulfilledNext(res)
							}
					}
				} catch (err) {
					// 如果函數執行出錯,新的Promise對象的狀态為失敗
					onRejectedNext(err)
				}
			}
			switch (_status) {
				// 當狀态為pending時,将then方法回調函數加入執行隊列等待執行
				case PENDING:
					this._fulfilledQueues.push(fulfilled)
					this._rejectedQueues.push(rejected)
					break
				// 當狀态已經改變時,立即執行對應的回調函數
				case FULFILLED:
					fulfilled(_value)
					break
				case REJECTED:
					rejected(_value)
					break
			}
		})
	}
	// 添加catch方法
	catch (onRejected) {
		return this.then(undefined, onRejected)
	}
	// 添加靜态resolve方法
	static resolve (value) {
		// 如果參數是MyPromise執行個體,直接傳回這個執行個體
		if (value instanceof MyPromise) return value
		return new MyPromise(resolve => resolve(value))
	}
	// 添加靜态reject方法
	static reject (value) {
		return new MyPromise((resolve ,reject) => reject(value))
	}
	// 添加靜态all方法
	static all (list) {
		return new MyPromise((resolve, reject) => {
			/**
			 * 傳回值的集合
			 */
			let values = []
			let count = 0
			for (let [i, p] of list.entries()) {
				// 數組參數如果不是MyPromise執行個體,先調用MyPromise.resolve
				this.resolve(p).then(res => {
					values[i] = res
					count++
					// 所有狀态都變成fulfilled時傳回的MyPromise狀态就變成fulfilled
					if (count === list.length) resolve(values)
				}, err => {
					// 有一個被rejected時傳回的MyPromise狀态就變成rejected
					reject(err)
				})
			}
		})
	}
	// 添加靜态race方法
	static race (list) {
		return new MyPromise((resolve, reject) => {
			for (let p of list) {
				// 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
				this.resolve(p).then(res => {
					resolve(res)
				}, err => {
					reject(err)
				})
			}
		})
	}
	finally (cb) {
		return this.then(
			value  => MyPromise.resolve(cb()).then(() => value),
			reason => MyPromise.resolve(cb()).then(() => { throw reason })
		);
	}
}
           

繼續閱讀