天天看點

手寫Promise原理實作

從構造函數開始

  1. Promise就是一個類,在執行這個類的時候需要傳遞一個執行器進去,執行器會立即執行。
  2. Promise中有三種狀态 分别為:成功

    fulfilled

    失敗

    rejected

    等待

    pending

    ,一旦狀态确定就不可更改。
//Promise 類核心邏輯實作
const PENDING = 'pending';//等待
const FULFILLED = 'filfulled';//成功
const REJECTED = 'rejected';//失敗

class Promise {
    //構造函數
    constructor(executor){
        //try catch為了捕獲執行器中的錯誤 進而抛出異常
        try{
            //執行器:立即執行
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    //promise狀态:先定義為peding,在resolve和reject中改變狀态
    status = PENDING;
	//成功之後的值
	value = undefined;
	//失敗後的原因
	reason = undefined;
	//成功回調函數隊列
	successCallback = [];
	//失敗回調函數隊列
	failCallback = [];
}

module.exports = Promise
           

resolve屬性的定義

resolve屬性用來修改Promise的狀态:pending => fulfilled

//構造函數中
resolve = value =>{
        //如果不是等待,則阻止程式向下執行
        if(this.status !=== 'pending') return;
        //将狀态更改為成功
        this.status = FULFILLED
        //儲存成功之後的值
        this.value = value
        //判斷成功回調是否存在 如果存在 => 調用:調用完後删除并傳回第一個回調的值
        while(this.successCallback.length) this.successCallback.shift()()
}
           

reject屬性的定義

reject屬性用來修改Promise的狀态:pending => rejected

//構造函數中 
reject = reason => {
        //如果狀态不是等待,阻止程式向下執行
        if(this.status !== 'pending') return;
        //将狀态更改為失敗
        this.status = REJECTED
        //儲存失敗之後的原因
        this.reason = reason
        //判斷失敗回調是否存在 如果存在 調用
        white(this.failCallback.length) this.failCallback.shift()()
    }
           

then方法的實作

  1. then方法是被定義在原型對象中的。
  2. then支援兩個參數,分别是成功和失敗的回調,而且這兩個參數可傳可不傳。
  3. then方法内部做的事情就是判斷狀态:如果狀态是成功,調用成功的回調函數 ,如果狀态是失敗,則調用失敗回調函數。
  4. 因為有異步任務的情況,并且支援多個回調,是以我們需要對回調函數采用資料進行儲存,是以上面定義了失敗和成功的回調函數隊列。
  5. then的傳回結果也是Promise對象
//構造函數中
then(successCallback,failCallback){
    //當調用then方法的時候不傳遞參數,則Promise狀态會依次向後傳遞,直到傳遞給有回調的then方法
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => { throw reason }
    
    let promise2 = new Promise((resolve,reject) => {
        let that = this
        if(this.status === FULFILLED){
            //為了變為異步操作
            setTimeout(()=>{
                //try catch為了捕獲錯誤,抛出異常
                try{
                    let x = successCallback(this.value)
                    //判斷x的值是普通值還是Promise對象:如果是普通值,直接調用resolve / 如果是Promise對象,檢視Promise對象傳回的結果。
                    //然後再根據Promise對象傳回的結果,決定調用resolve 還是調用reject
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            },0)
        }else {
            //有異步任務時:等待
            //将成功回調和失敗回調儲存起來
            this.successCallback.push(()=>{
                //為了變為異步操作
                setTimeout(()=>{
                	//try catch為了捕獲錯誤,抛出異常
                	try{
                    	let x = successCallback(this.value)
                    	//判斷x的值是普通值還是Promise對象:如果是普通值,直接調用resolve / 如果是Promise對象,檢視Promise對象傳回的結果。
                    	//然後再根據Promise對象傳回的結果,決定調用resolve 還是調用reject
                    	resolvePromise(promise2,x,resolve,reject)
                	}catch(e){
                    	reject(e)
                	}
            	},0)
            });
            this.failCallback.push(()=>{
                //為了變為異步操作
            	setTimeout(()=>{
                	//try catch為了捕獲錯誤,抛出異常
                	try{
                    	let x = successCallback(this.value)
                    	//判斷x的值是普通值還是Promise對象:如果是普通值,直接調用resolve / 如果是Promise對象,檢視Promise對象傳回的結果。
                    	//然後再根據Promise對象傳回的結果,決定調用resolve 還是調用reject
                    	resolvePromise(promise2,x,resolve,reject)
                	}catch(e){
                    	reject(e)
                	}
            	},0)
            })
        }
    })
    return promise2;
}


function resolvePromise(promise2,x,resolve,reject){
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof Promise){
        //Promise對象
        x.then(resolve,reject)
    }else {
        //普通值
        resolve(x)
    }
}
           

catch方法的實作

  1. catch方法用來處理目前這個Promise最終的狀态為失敗的情況。
  2. 當我們調用then方法時 是可以不傳遞失敗回調的 那麼失敗回調就會被catch捕獲并傳入catch方法的回調函數。
  3. catch不是靜态方法,需要定義在Promise的原型上
//構造函數中
catch(failCallback){
    return this.then(undefined,failCallback)
}
           

finally方法的實作

  1. 無論目前的這個Promise對象是成功的還是失敗的 finally當中的函數總會被執行一次。
  2. finally方法的後面我們可以鍊式調用then方法 來得到目前這個Promise最終傳回的結果。
  3. finally不是靜态方法,需要定義在Promise的原型上
  4. 需要實作鍊式調用
//構造函數中
finally(callback){
    //調用then方法來得到目前Promise的狀态
    //因為then方法就傳回一個Promise,是以直接return
    //要等待異步操作完成後,後面的then才能執行
    return this.then(value => {
		//如果callback傳回值是普通值,那麼就轉換成Promise等待執行完成,後面的then再執行,如果傳回的是Promise 還是等待執行完成在執行
        return Promise.resolve(callback()).then(()=>value)
    },reason => {
        return Promise.resolve(callback()).then(()=>{ throw reason })
    })
}
           

Promise.resolve方法的實作

  1. Promise.resolve 的作用就是将裡面的值轉化為promise對象。
  2. Promise.resolve 是一個靜态方法。
//構造函數中
static resolve(value){
    //判斷是普通值還是Promise對象,如果是Promise對象就原封不動的放回出去,如果是普通值,那就建立一個Promise将普通值包裹進去傳回出去。
    if(value instanceof Promise) return value;
    return new Promise(resolve => resolve(value));
}
           

Promise.reject方法的實作

  1. Promise.reject()方法的參數,會原封不動地作為reject的理由,變成後續方法的參數。這一點與Promise.resolve方法不一緻
  2. Promise.reject是一個靜态方法。
// Promise.reject()方法的參數,會原封不動地作為reject的理由,變成後續方法的參數。
  // 這一點與Promise.resolve方法不一緻
  static reject(reason) {
    return new Promise((undefined, reject) => reject(reason));
  }
           

Promise.all方法的實作

  1. Promise.all方法是解決異步并發問題的,它允許我們按照異步調用的順序來執行異步方法。
  2. Promise.all是一個靜态方法,接收一個數組作為參數。
  3. Promise.all 後面也可以.then鍊式調用。
//構造函數中
static all(array){
    //準備一個結果數組
    let result = [];
    let index = 0;
    
    //Promise.all 後面也可以.then  是以先傳回個Promise對象
    return new Promise((resolve,reject) => {
        //向結果數組中添加傳回值
		function addData(key,value){
            result[key] = value
            index ++;
            //for循環是同步執行循環體的,是以我們必須等待異步操作完成之後才能去傳回結果
            // 如果index 等于 數組的長度,代表所有的操作已經完畢,就可以傳回結果了
            if(index === array.length){
                resolve(result);
            }
        }
        //通過循環的方式,循環進來的資料,在循環過程中判斷了循環目前值,判斷其是普通值還是Promise對象
        for(let i=0;i<array.length;i++){
            let current = array[i]
            //判斷目前的值是普通值還是Promise對象
            if(current instanceof Promise){
                //注:在執行過程中别忘記有異步任務,for循環是不會等待異步操作的,是以當我們調用resolve的時候,還沒有執行完,是以結果中會有空值。
                //Promise對象:先執行Promise對象,如果執行成功調用addData方法,反之reject()
                current.then(value => addData(i,value),reason => reject(reason))
            }else {
                //普通值:對應放入結果數組中
                addData(i,array[i])
            }
        }
    })
}
           

Promise.race方法的實作

// race方法
static race (array) {
// race方法,取數組中第一個傳回的結果,無論成功或者失敗
return new Promise((resolve, reject) => {
  array.forEach(current => {
    if (current instanceof Promise) {
      // promsie對象,傳回成功或者失敗的結果
      current.then(value => resolve(value), reason => reject(reason))
    } else {
      // 普通值 ,直接傳回
      resolve(current)
    }
  })
})
}
           

完整代碼

const PENDING = 'pending';//等待
const FULFILLED = 'filfulled';//成功
const REJECTED = 'rejected';//失敗

class Promise {
    constructor(executor){
        try{
            executor(this.resolve,this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    status = PENDING;
	value = undefined;
	reason = undefined;
	successCallback = [];
	failCallback = [];

	resolve = value =>{
        if(this.status !=== 'pending') return;
        this.status = FULFILLED
        this.value = value
        while(this.successCallback.length) this.successCallback.shift()()
	}
    
    reject = reason => {
        if(this.status !== 'pending') return;
        this.status = REJECTED
        this.reason = reason
        white(this.failCallback.length) this.failCallback.shift()()
    }
    
    then(successCallback,failCallback){
    	successCallback = successCallback ? successCallback : value => value
    	failCallback = failCallback ? failCallback : reason => { throw reason }
    
    	let promise2 = new Promise((resolve,reject) => {
        	let that = this
        	if(this.status === FULFILLED){
            	setTimeout(()=>{
                	try{
                    	let x = successCallback(this.value)
                    	resolvePromise(promise2,x,resolve,reject)
                	}catch(e){
                    	reject(e)
                	}
            	},0)
        	}else {
            	this.successCallback.push(()=>{
                	setTimeout(()=>{
                		try{
                    		let x = successCallback(this.value)
                    		resolvePromise(promise2,x,resolve,reject)
                		}catch(e){
                    		reject(e)
                		}
            		},0)
            	});
            	this.failCallback.push(()=>{
            		setTimeout(()=>{
                		try{
                    		let x = successCallback(this.value)
                    		resolvePromise(promise2,x,resolve,reject)
                		}catch(e){
                    		reject(e)
                		}
            		},0)
            	})
        	}
    	})
    	return promise2;
	}


	function resolvePromise(promise2,x,resolve,reject){
    	if(promise2 === x){
        	return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    	}
    	if(x instanceof Promise){
        	x.then(resolve,reject)
    	}else {
        	resolve(x)
    	}
	}

	catch(failCallback){
    	return this.then(undefined,failCallback)
	}
	
	finally(callback){
    	return this.then(value => {
        	return Promise.resolve(callback()).then(()=>value)
    	},reason => {
        	return Promise.resolve(callback()).then(()=>{ throw reason })
    	})
	}

	static resolve(value){
    	if(value instanceof Promise) return value;
    	return new Promise(resolve => resolve(value));
	}

  	static reject(reason) {
    	return new Promise((undefined, reject) => reject(reason));
  	}

    static race (array) {
        return new Promise((resolve, reject) => {
          array.forEach(current => {
            if (current instanceof Promise) {
              current.then(value => resolve(value), reason => reject(reason))
            } else {
              resolve(current)
            }
          })
        })
    }

	static all(array){
    	let result = [];
    	let index = 0;
    
    	return new Promise((resolve,reject) => {
			function addData(key,value){
            	result[key] = value
            	index ++;
            	if(index === array.length){
                	resolve(result);
            	}
        	}
        	for(let i=0;i<array.length;i++){
            	let current = array[i]
            	if(current instanceof Promise){
                	current.then(value => addData(i,value),reason => reject(reason))
            	}else {
                	addData(i,array[i])
            	}
        	}
    	})
	}
}

module.exports = Promise
           

繼續閱讀