都2020了,也該會本身手寫一個promise了

寫在前面

今天在看文章都時候發現了手寫promise的要求,捫心自問,我靠,我竟然一點思路都沒有。這可不行,必須本身手擼一發,面的對不起本身的左右手。哎,又要學習,好累,不過還好,想一想都2020了,還不會本身手寫promise,着實有點垃圾,仍是趕快搞起來吧。javascript


promise的幾點要求

  1. 什麼是promise?
  2. promise解決的什麼問題?
  3. 咱們本身實現一個promise。

什麼是promise?

Promise是一種異步操做的解決方案,將寫法複雜的傳統的回調函數和監聽事件的異步操做,用同步代碼的形式表達出來。避免了多級異步操做的回調函數嵌套。簡單的來講就是爲了解決回調地獄的問題。試想一下,下面這個代碼你看這頭疼不?若是在加幾層呢就會出現金字塔的代碼了,這樣給咱們閱讀代碼和維護代碼形成的很大的困擾。java

function a() {
  function b() {
    function c() {
      function d() {}
      d();
    }
    c();
  }
  b();
}
a();
複製代碼

promise解決了什麼問題?

咱們將上面的代碼用promise改寫。這樣是否是好多了。promise

a().then(b).then(c).then(d)
複製代碼

實現一個Promise

咱們按照promiseA+的規範來寫。不知道的同窗能夠看看哦。異步

Promise類

const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected
class Promise {
	constructor(exextuor) {
		this.exextuor = exextuor
		this.states = PENDING
		this.onResolveCallbacks = []
		this.onRejectCallbacks = []
	}
	function resolve() {}
	function reject() {}
	
	if(exextuor) {
		try {
			exextuor(resolve, reject)
		}catch(e) {
			reject(e)
		}
	}
}

複製代碼

上面的代碼很簡單,咱們一個resolve的調度棧,一個reject的調度棧,聲明resolve和reject函數,並執行exextuor若是報錯,將reject執行。 ####resolve和reject函數(2.1規範) 一個 promise 有且只有一個狀態(pending,fulfilled,rejected 其中之一) pending 狀態時可能會轉變爲 fulfilled 或 rejected 狀態。fulfilled 狀態時:不能再狀態爲任何其餘狀態,必須有一個 value,且不可改變。 rejected 狀態時:不能再狀態爲任何其餘狀態,必須有一個 reason,且不可改變。函數

function resolve(value) {
	if(value instanceof Promise) {
		try{
			value.then(resolve, reject)
		}catch(e) {
			reject(e)
		}
	}
	if(this.states = PENDING) {
		this.states = FULFILLED
		this.value = value
		this.onResolveCallbacks.forEach(cb => cb(this.value))
	}
}
function reject(reason) {
	if(this.states = PENDING) {
		this.states = REJECTED
		this.reason = reason
		this.onRejectCallbacks.forEach(cb => cb(this.reason))
	}
}
複製代碼

then函數(2.2規範)

一個 promise 必須提供一個 then 方法,用來獲取當前或最終的 value 或 reason,一個 promise 的 then 方法接受兩個參數:promise.then(onFulfilled, onRejected)學習

then(onFulfilled, onRejected) {
	onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
	onRejected = typeof onRejected === "function" ? onRejected : reason => throw reason
	if(this.states = FULFILLED) {
		this.x = onFulfilled(this.value)
	}
	if(this.states = REJECTED) {
		this.x = onRejected(this.reason)
	}
	if(this.states = PENDING) {
		this.onResolveCallbacks.push(() => {
			this.x = onFulfilled(this.value)
		})
		this.onRejectCallbacks.push(() => {
			this.x = onRejected(this.reason)
		})
	}
複製代碼

then函數的鏈式調用

promise 的 then 能夠鏈式調用屢次,若是或當 promise 轉態是 fulfilled 時,全部的 onFulfilled 回調回以他們註冊時的順序依次執行。若是或當 promise 轉態是 rejected 時,全部的 onRejected 回調回以他們註冊時的順序依次執行。測試

let primise2
if(this.states = FULFILLED) {
	return primise2 = new Promise((resole, reject) => {
		// 若是在執行onFulfilled的時候有異常咱們要將primise2的reject執行
		try {
			let x = onFulfilled(this.value)
			// 對返回值進行處理,由於這個值多是個promise
			resolvePromise(primise2, x, resole, reject)
		} catch(e) {
			reject(e)
		}
	})
}
複製代碼

注意上面的三個判斷的處理基本同樣,咱們都要對結果處理。resolvePromise一下。 ####resolvePromise(2.3規範) 2.3.1 若是返回的 promise1 和 x 是指向同一個引用(循環引用),則拋出錯誤 2.3.2 若是 x 是一個 promise 實例,則採用它的狀態ui

  1. 若是 x 是 pending 狀態,那麼保留它(遞歸執行這個 promise 處理程序),直到 pending 狀態轉爲 fulfilled 或 rejected 狀態。
  2. 若是或當 x 狀態是 fulfilled,resolve 它,而且傳入和 promise1 同樣的值 value。
  3. 若是或當 x 狀態是 rejected,reject 它,而且傳入和 promise1 同樣的值 reason
const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected

function resolvePromise(promise2, x, resolve, reject) {
	if(promise2 === x) {
		return reject(new TypeError('循環引入!'))
	}
	
	// 用於resolve, reject只調用一次
	let called = false
	
	// x是一個promise
	if(x instanceof Promise) {
		if(x.status === PENDING) {
			x.then((y)=>{
				// 有可能y仍是一個promise
				resolvePromise(promise2, y, resolve, reject)
			}, reject)
		} else {
			x.then(resolve, reject)
		}
	} else if(x !== null && (typeof x === 'function' || typeof x === 'object')) {
	// x是一個thenable的對象
		try{
			// 有可能在區x.then的時候異常
			let then = x.then
			if(typeof x === 'function') {
				// 這是咱們本身的promise和別人的promise的交互
				then.call(x, (z)=>{
					// 有可能z仍是一個promise
					if(called) return
					called = true
					resolvePromise(promise2, z,resolve, reject)
				}, (err)=>{
					if(called) return
					called = true
					reject(err)
				})
			}else {
				// 說明x是一個普通對象
				resolve(x)
			}
		}catch(e) {
			reject(e)
		}
	} else {
		// x是個普通值
		if(called) return
		called = true
		resolve(x)
	}
}
複製代碼

catch函數

catch(onReject) {
	this.then(null, onReject)
}
複製代碼

異步調用(3.1規範)

3.1 這裏的 「平臺代碼」是指引擎,環境,和 promise 實現代碼。實際上,這個要求確保 onFulfilled 和 onRejected 都在下一輪的事件循環中(一個新的棧)被異步調用。能夠用宏任務,例如:setTimeout,setImmediate 或者微任務,例如:MutationObsever 或 process.nextTick 實現。 因爲 promise 的實現被當作平臺代碼,因此它自己可能包含一個任務隊列或 「trampoline」 的處理程序this

// 因此咱們要在全部resolve和reject的調用上加一個setTimeout()
// 咱們在resolve函數外面包裹一個
// 同理咱們也要在reject函數包裹一下
function resolve() {
	setTimeout(()=>{
		// 上面的內容同樣
	})
}
複製代碼

promise.all

Promise.all = function(promises) {
	return new Promise((resolve, reject)=>{
		let res = []
		let count = 0
		function done(i, result) {
			res[i] = result
			if(++count === promises.length) {
				resolve(res)
			}
		}
		for(let i = 0; i<promises.length; i++) {
			done.call(null, i,res)
		}
	}, reject)
}
複製代碼

測試結果

Promise.deferred = Promise.deferred = function() {
	let defer = {}
	defer.promise = new Promise((resolve, reject) => {
		defer.resolve = resolve
		defer.reject = reject
	})
	return defer
}
複製代碼

咱們測試用用例測試。基本全部的用例所有經過。好了這樣一個promise基本實現。
相關文章
相關標籤/搜索