Promise源碼解析-步步爲營皆可及

本文根據promise的用法, 帶您一步一步的瞭解promise的源碼內幕; 本文采用要點、使用、源碼分析的思路,步步爲營,一點一點的剖析Promise的神祕世界; 本文Promise源碼部分採用es5語法es6

Promise定義

  • promise是異步解決方案; 字面意思是"承諾",即將來某個時刻才能獲得結果;
  • 從語法上來講,它是一個對象,從它能夠獲取異步操做的消息;

基本用法 - 構造函數

ES6 規定,Promise對象是一個構造函數,用來生成Promise實例json

  • 使用:
let p = new Promise();
複製代碼
  • 源碼
function Promise(){ //... }
複製代碼
  • 解析: 構造函數與普通的函數並沒有二致, 只是咱們在使用時須要經過new的方式來調用; 其中構造函數中的this指向new生成的對象實例;

Promise構造函數接收一個執行器函數(executor)做爲參數, 該函數的兩個參數分別爲resolve和reject。當new生成對象實例的時候,Promise的參數executor會當即執行數組

  • 使用:
let p = new Promise((resolve, reject)=>{
		// coding....
	});
複製代碼
  • 源碼:
function Promise(executor){
		
		executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	}
複製代碼
  • 解析: executor即調用時候傳遞進來的函數 (resolve, reject)=>{ // coding... }, 調用時會傳遞兩個參數進去, 供// coding....處使用

執行器executor的參數(resolve, reject)是兩個函數, 這兩函數的做用:promise

  • resolve函數是將Promise對象的狀態從"pending"變爲"resolved"('未完成' => '成功'), 在異步操做成功時調用,並將異步操做結果做爲參數傳遞出去(源碼的resolve函數中接收);
  • reject函數 是將Promise對象的狀態從"pending"變爲"rejected"('未完成' => '失敗'), 在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去(源碼的reject函數中接收);
  • 使用:
let p = new Promise((resolve, reject)=>{
	    // coding....
	    resolve('成功');
	    // 或 reject('失敗');
	});	

複製代碼
  • 源碼:
function Promise(executor){
	
	    function resolve(value){
	        console.log(value);   	// 調用resolve('成功'); 	=> 成功
	    }
	 
	    function reject(reason){
	        console.log(reason);		// 調用reject('失敗');	=> 失敗 
	    }
	
	    executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	}
複製代碼
// 升級(添加狀態)
	function Promise(executor){
		this.status = 'pending';		// 用於保存promise實例的狀態,默認爲pending,等待態
		this.value 	= undefined;		// 保存成功時的值
		this.reason = undefined;		// 保存失敗時的緣由

	    function resolve(value){
	        console.log(value);   	// 調用resolve('成功'); 	=> 成功
			this.value = value;
	    }
	 
	    function reject(reason){
	        console.log(reason);		// 調用reject('失敗');	=> 失敗 
			this.reason = reason;
	    }
	
	    executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	}
複製代碼
// 升級(狀態的轉變) pending => fulfilled 或 pending => rejected
	//一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果[摘自:http://es6.ruanyifeng.com/#docs/promise]

	function Promise(executor){
		let self 	= this;
	    this.status = 'pending';  // pending => fulfilled | rejected
	    this.value  = undefined;
	    this.reason = undefined;
	    // 成功 
	    function resolve(value){
	        if(self.status === 'pending'){
	            self.value  = value;
	            self.status = 'fulfilled';
	            console.log(self.status)
	        }  
	    }
	
	    function reject(reason){
	        if(self.status === 'pending'){
	            self.reason = reason;
	            self.status = 'rejected';
	            console.log(self.reason)
	        }
	    }
	
		// 須要經過別名引用的方式; 由於resolve和reject中的this指向的是全局對象
	    executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	}

複製代碼
  • 解析:bash

    1. promise有三個狀態: pending(進行中或等待中)、fulfilled(已成功)、rejeced(已失敗)
    2. 使用時調用resolve函數,會把promise的狀態轉變爲fulfilled,而且將成功的結果保存到promise實例的value屬性
    3. 使用時調用reject函數,會把promise的狀態轉變爲rejected,而且將失敗的緣由保存到promise實例的reason屬性
    4. 狀態的轉變只能從pending => fulfilled 或 rejected, fulfilled和rejected之間是不能轉換的;因此在resolve和rejected函數中添加了狀態判斷
    5. 調用resolve函數或reject函數會修改promise實例value(成功的結果)屬性或reason(失敗的緣由),而此時在resolve和reject內部如何引用到promsie實例呢? -> 在Promise構造函數內部設置this的引用: let self = this; 在resolve或reject函數中,經過self引用promise實例;
  • 小結: 代碼書寫到此刻,咱們打印Promise實例, 便可查看到當前實例的狀態和相關屬性;異步

let p = new Promise((resolve, reject)=>{
    resolve('成功');
	/*
	  if (/* 異步操做成功 */){
	    resolve(value);
	  } else {
	    reject(error);
	  }
	*/
});
console.log(p);  // Promise { status: 'fulfilled', value: '成功', reason: undefined }

複製代碼

若是Promise的執行器函數(executor)裏面直接拋出異常呢? 捕獲錯誤,相對於執行了reject函數

  • 使用:
let p = new Promise((resolve, reject)=>{
	throw new Error('錯誤扔給你, 來打我呀^_^');
});
複製代碼
  • 源碼:
function Promise (executor){
    // 在promise內部定義一個狀態 當前promise的狀態
    let self = this;
    self.value = undefined;
    self.reason = undefined
    self.status = 'pending'; // 默認promise的狀態是pengding
    self.onResolevedCallbacks = []; // 存放全部成功的回調
    self.onRejectedCallbacks = []; // 存放全部失敗的回調
    function resolve(value){
        // (value!=null && typeof value === 'object') || typeof value == 'function'
        if(value instanceof Promise){
            console.log('here');
            // if(value.then && typeof value.then === 'function'){
                return value.then((data)=>{
                    console.log('11111111111')
                    console.log(data)
                    resolve(data)
                },y=>{
                    reject(y);
                });
            // }
        }
        console.log(self.status)
        if(self.status === 'pending'){
            self.value = value;
            console.log('pending:',value)
            self.status = 'resolved'; // 成功態
            console.log('pending--', self.onResolevedCallbacks)
            self.onResolevedCallbacks.forEach(fn=>fn());
        }
    }
    function reject(reason){
        if(self.status === 'pending'){
            self.reason = reason;
            self.status = 'rejected'; // 失敗態
            // 發佈
            self.onRejectedCallbacks.forEach(fn =>fn());
        }
    }

	// ---------------修改在這裏-------------------------------------------------------------------------
	// 小樣,敢給大爺拋錯誤, 直接捕獲了,讓你的錯誤銷聲匿跡 @<_>@
    try{
        executor(resolve,reject); // 用戶會調用resolve || reject
    }catch(e){
        reject(e); // 說明失敗了
    }
}
複製代碼
  • 解析:

上面代碼理解了,請移步下一個關 注意,爲了行文方便,後面的resolved統一隻指fulfilled狀態,不包含rejected狀態源碼分析


Promise.prototype.then()

Promise.prototype.then()方法是Promise的核心方法; Promise實例生成之後,能夠用then方法分別指定resolved狀態和rejected狀態的回調函數;then方法的做用就是爲promise實例添加狀態改變時的回調函數;post

  • 使用:
let p = new Promise((resolve, reject)=>{
    resolve('成功');
    // reject('失敗');
});

p.then((value)=>{
    // 成功時執行的回調
    console.log('執行resolve,輸出實例成功態的vlaue屬性: ', value);
}, (reason)=>{
    // 失敗時執行的回調
    console.log('執行reject,輸出實例失敗態的reason屬性: ', reason);
});

// => 執行resolve,輸出實例成功態的vlaue屬性:  成功
複製代碼
  • 源碼:
function Promise(executor){
	
	let self = this;
    self.status = 'pending';  // pending => fulfilled | rejected
    self.value  = undefined;
    self.reason = undefined;
    // 成功 
    function resolve(value){
        if(self.status === 'pending'){
            self.value  = value;
            self.status = 'fulfilled';
        }  
    }

    function reject(reason){
        if(self.status === 'pending'){
            self.reason = reason;
            self.status = 'rejected';
        }
    }
	
    try {
        executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
    }catch(err){
        reject(err);
    }
}

// Promise的核心方法
Promise.prototype.then = function(onFulfilled, onRejected){
    if(this.status === 'fulfilled'){
        onFulfilled(this.value); // 這裏傳遞成功的值
    }

    if(this.status === 'rejected'){
        onRejected(this.reason);  // 這裏傳遞失敗的緣由或錯誤
    }
};

複製代碼
  • 解析:
    1. then方法能夠接收兩個回調函數做爲參數; 1> Promise實例對象的狀態變爲resolved時調用 2> Promsie實例的狀態變爲rejected時調用[可選];
    2. then的成功回調函數接收promise對象(也就是promise實例)的成功時的值做爲參數(用於處理->執行);
    3. then的失敗回調函數接收promise對象的失敗時的緣由或錯誤做爲參數;

Promise.prototype.then() 與異步調用; 咱們知道Promise主要是用來處理異步的, 當promise對象的狀態在一段時間後發生變化時, 就會觸發then方法綁定的回調函數; 這裏要注意幾點:學習

1> then方法在調用時會當即執行(同步), 關鍵在於then不一樣狀態的回調函數只有在狀態發生改變時執行(異步); 
2> Promise是同步的, 實例新建後會當即執行; then是異步的,當狀態改變時才執行
複製代碼
let promise = new Promise(function(resolve, reject) {
	  console.log('Promise');
	  resolve();
	});
	
	promise.then(function() {
	  console.log('resolved.');
	});
	
	console.log('Hi!');
	// => 依次輸出: 'Promise'  'Hi!'  'resolved.'
複製代碼
  • 使用:
let p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('成功');			// 2秒鐘以後,讓promise的狀態變成成功態
    }, 2000);
});

p.then((value)=>{
    // 成功時執行的回調
    console.log('2秒後執行resolve,輸出實例成功態的vlaue屬性: ', value);
}, (reason)=>{
    // 失敗時執行的回調
    console.log('執行reject,輸出實例失敗態的reason屬性: ', reason);
});
複製代碼
  • 源碼:
function Promise(executor){
		let self = this;
	    self.status = 'pending';  // pending => fulfilled | rejected
	    self.value  = undefined;
	    self.reason = undefined;
	
	    self.onFulfilledCallbacks = [];     // 用於存放全部then方法成功態的回調
	    self.onRejectedCallbacks = [];      // 用於存放全部then方法失敗態的回調
	    // 成功 
	    function resolve(value){
	        if(self.status === 'pending'){
	            self.value  = value;
	            self.status = 'fulfilled';
	            self.onFulfilledCallbacks.forEach(fn=>fn()); // 當狀態變爲resolved時執行訂閱的函數(then方法成功時的回調)
	        }  
	    }
	
	    function reject(reason){
	        if(self.status === 'pending'){
	            self.reason = reason;
	            self.status = 'rejected';
	            self.onRejectedCallbacks.forEach(fn=>fn()); // 當狀態變爲rejected時執行訂閱的函數(then方法失敗時的回調)
	        }
	    }
	
	    try {
	        executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	    }catch(err){
	        reject(err);
	    }
	}
	
	
	Promise.prototype.then = function(onFulfilled, onRejected){
	    if(this.status === 'fulfilled'){
	        onFulfilled(this.value); // 這裏傳遞成功的值
	    }
	
	    if(this.status === 'rejected'){
	        onRejected(this.reason);  // 這裏傳遞失敗的緣由或錯誤
	    }
	
		// 在異步執行完成以前,promise的狀態爲pending,此時會把then兩種狀態的回調先存儲起來,待狀態改變後執行
	    if(this.status === 'pending'){
	        this.onFulfilledCallbacks.push(()=>{
	            onFulfilled(this.value);
	        });
	        this.onRejectedCallbacks.push(()=>{
	            onRejected(this.reason);
	        });
	    }
	};

複製代碼
  • 解析:

    1. 回調的存儲和調用是一個典型的發佈/訂閱模式;promise處於等待態時訂閱,狀態改變後執行發佈;
    2. 在異步執行完成以前,此時promise的狀態爲pending,此時咱們把then函數的參數(成功的回調resove和失敗reject的回調)存儲起來,待異步完成狀態改變後調用;
    3. 用於存儲then回調的是兩個數組,一個是成功回調函數的數組,一個是失敗回調函數的數組;
    4. 若是調用resolve函數和reject函數時帶有參數, 那麼它們的參數會被傳遞給then的回調函數; 若是沒傳具體的參數,那就將undefined傳遞過去;
    5. reject函數的參數一般是Error實例,表示拋出異常或錯誤;
    6. resolve函數的參數除了普通值之外, 還能夠是另一個Promise

then方法返回的是一個新的Promise實例(該實例不是原來那個Promise實例);

  • 使用(同步狀況下的鏈式調用):
let p = new Promise((resolve, reject)=>{
	    resolve(420);
	});
	
	p.then((value)=>{
	    // 成功時執行的回調
	    console.log('2秒後執行resolve,輸出實例成功態的vlaue屬性: ', value); // 2秒後執行resolve,輸出實例成功態的vlaue屬性:  420
	    return value + 100;
	}, (reason)=>{
	    // 失敗時執行的回調
	    console.log('執行reject,輸出實例失敗態的reason屬性: ', reason); 
	}).then((value)=>{
	    // then的成功回調裏面輸出,前一個then成功回調的返回值(即promise實例的value屬性), 若是沒有顯示的return, 那麼返回的是undefined
	    console.log('第二個then的成功回調裏面輸出,前一個then成功回調的返回值: ',value); // 第二個then的成功回調裏面輸出,前一個then成功回調的返回值:  520
	    return value + 200;
	}).then((value)=>{
	    console.log('第三個then的成功回調裏面輸出,前一個then成功回調的返回值: ',value);  // 第三個then的成功回調裏面輸出,前一個then成功回調的返回值:  720
	});

複製代碼
  • 源碼:
function Promise(executor){
		
		let self = this;
	    self.status = 'pending';  // pending => fulfilled | rejected
	    self.value  = undefined;
	    self.reason = undefined;
	
	    self.onFulfilledCallbacks = [];     // 用於存放全部then方法成功態的回調
	    self.onRejectedCallbacks = [];      // 用於存放全部then方法失敗態的回調
	    // 成功 
	    function resolve(value){
	        if(self.status === 'pending'){
	            self.value  = value;
	            self.status = 'fulfilled';
	            self.onFulfilledCallbacks.forEach(fn=>fn());
	        }  
	    }
	
	    function reject(reason){
	        if(self.status === 'pending'){
	            self.reason = reason;
	            self.status = 'rejected';
	            self.onRejectedCallbacks.forEach(fn=>fn());
	        }
	    }
	
	    try {
	        executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	    }catch(err){
	        reject(err);
	    }
	}
	
	
	Promise.prototype.then = function(onFulfilled, onRejected){
	    let self = this;
	    let promise2 = new Promise((resolve, reject)=>{
	        console.log(this === self); // 這裏由於使用了箭頭函數,因此self === this, 指向同一個對象; 爲了避免混淆,後面將採用self的方式
	        // 該executor函數會裏面執行,所以把以前的狀態判斷及成功回調的代碼移到此處與以前功能同樣;
	        // 須要說明的是這個promise須要根據上一個then的狀態和值進行判斷,故而設置self變量用於引用上一個this
	        if(this.status === 'fulfilled'){
	            let x = onFulfilled(this.value); // 成功回調; 這裏的value指的是第一個Promise實例的value屬性
	            resolve(x);   // 將處理後的結果做爲參數傳遞給promise實例的value屬性; 同時也會傳遞給下一個then的成功回調
	        }
	    
	        if(this.status === 'rejected'){
	            try{
	                let x = onRejected(this.reason);  // onRejected處理 
	                reject(x);
	            }catch(e){
	                reject(e);
	            }
	
	        }
	    
	        if(this.status === 'pending'){
	            this.onFulfilledCallbacks.push(()=>{
	                onFulfilled(this.value);
	            });
	            this.onRejectedCallbacks.push(()=>{
	                onRejected(this.reason);
	            });
	        }
	    });
	
	
	    return promise2;
	};
複製代碼
  • 解析:

    1. then鏈式調用的關鍵是:then方法調用後返回一個新的Promise實例;
    2. 生成promise2實例時傳遞到Promise構造函數中的執行器excutor, 它裏面用於判斷狀態的是上一個promise實例的狀態,操做的都是上一個promise實例的屬性;
    3. 有多少個then,就至少有多少個promise實例;
    4. 不論有多少個promise實例,在new新的Promise實例時都是判斷前一個promise實例的狀態、操做前一個promise實例的屬性, 把前面的promise實例上的屬性更新後, 傳遞到後面的then回調函數裏面;
    5. 不管多少個then,都是前一個回調函數完成之後,將返回結果做爲參數,傳入下一個then的對應的回調中;
    6. 不管多少個then,某一狀態的回調都是剛在一個數組,而後挨個的執行; 前一個執行完成後把實例屬性傳遞給下一個回調函數

Promise.prototype.then() 與異步調用

  • 使用(異步狀況下的鏈式調用):
let p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(420);
    }, 3000);
});

p.then((value)=>{
    // 成功時執行的回調
    console.log('2秒後執行resolve,輸出實例成功態的vlaue屬性: ', value); // 2秒後執行resolve,輸出實例成功態的vlaue屬性:  420
    return value + 100;
}, (reason)=>{
    // 失敗時執行的回調
    console.log('執行reject,輸出實例失敗態的reason屬性: ', reason); 
    throw new Error('失敗了');
}).then((value)=>{
    // then的成功回調裏面輸出,前一個then成功回調的返回值(即promise實例的value屬性), 若是沒有顯示的return, 那麼返回的是undefined
    console.log('第二個then的成功回調裏面輸出,前一個then成功回調的返回值: ',value); // 第二個then的成功回調裏面輸出,前一個then成功回調的返回值:  520
    return value + 200;
}, (reason)=>{
    console.log('第二個then的失敗: ', reason);
}).then((value)=>{
    console.log('第三個then的成功回調裏面輸出,前一個then成功回調的返回值: ',value);  // 第三個then的成功回調裏面輸出,前一個then成功回調的返回值:  720
});

// => 
		2秒後執行resolve,輸出實例成功態的vlaue屬性:  420
		第二個then的成功回調裏面輸出,前一個then成功回調的返回值:  520
		第三個then的成功回調裏面輸出,前一個then成功回調的返回值:  720
複製代碼
  • 源碼
function Promise(executor){
		let self 	= this;
	    self.status = 'pending';  // pending => fulfilled | rejected
	    self.value  = undefined;
	    self.reason = undefined;
	    self.onFulfilledCallbacks = [];     // 用於存放全部then方法成功態的回調
	    self.onRejectedCallbacks = [];      // 用於存放全部then方法失敗態的回調
	    // 成功 
	    function resolve(value){
	        if(self.status === 'pending'){
	            self.value  = value;
	            self.status = 'fulfilled';
	            self.onFulfilledCallbacks.forEach(fn=>fn());
	        }  
	    }
	
	    function reject(reason){
	        if(self.status === 'pending'){
	            self.reason = reason;
	            self.status = 'rejected';
	            self.onRejectedCallbacks.forEach(fn=>fn());
	        }
	    }
	
	    try {
	        executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	    }catch(err){
	        reject(err);
	    }
	}
	
	
	Promise.prototype.then = function(onFulfilled, onRejected){
	    let self = this;
	    let promise2 = new Promise(function(resolve, reject){
	        // console.log(this === self); // 這裏由於使用了箭頭函數,因此self === this, 指向同一個對象
	        // 該executor函數會裏面執行,所以把以前的狀態判斷及成功回調的代碼移到此處與以前功能同樣;
	        // 須要說明的是這個promise須要根據上一個的狀態和值進行判斷,故而設置self變量用於引用上一個this
	        if(self.status === 'fulfilled'){
	            let x = onFulfilled(self.value); // 成功回調; 若是沒有就當前對象的value屬性值
	            resolve(x);   // 將處理後的結果做爲參數傳遞給promise實例的value屬性; 同時也會傳遞給下一個then的成功回調
	        }
	    
	        if(self.status === 'rejected'){
	            try{
	                let x = onRejected(self.reason);  // onRejected處理
	                reject(x);
	            }catch(e){
	                reject(e);
	            }
	        }
	    
	        if(self.status === 'pending'){
	            self.onFulfilledCallbacks.push(()=>{
	                let x = onFulfilled(self.value);
	                resolve(x);
	            });
	            self.onRejectedCallbacks.push(()=>{
	                let x = onRejected(self.reason);
	                reject(x);
	            });
	        }
	    });
	    return promise2;
	};
複製代碼
  • 解析

    1. 異步和同步的處理方式幾乎一致, 異步的無非就是在pending態時先把回調保存起來,待狀態改變時再執行

Promise.prototype.then()的成功回調返回一個新的promise實例和執行executor時resolve一個新的promsie實例

採用鏈式的then,能夠指定一組按照次序調用的回調函數; 此時前一個回調函數,有可能返回的仍是一個Promise對象(即有異步操做),這時後一個回調函數,就會等待該Promise對象的狀態發生變化,纔會被調用;

所以處邏輯\代碼比較多,放在一塊兒看了

  • 使用
let p = new Promise((resolve, reject)=>{
	    resolve(1000);
		// 或 resolve(new Promise((resolve, reject)=>{ resolve('成功');}))
	});
	
	p.then((value)=>{
	    return new Promise((resolve, reject)=>{
	        resolve(value + 500);
	    });
	}).then((value)=>{
	    console.log(value);  // 1500
	});
複製代碼
  • 源碼
function Promise(executor){
	    let self = this;
	    this.status = 'pending';  // pending => fulfilled | rejected
	    this.value  = undefined;
	    this.reason = undefined;
	    this.onFulfilledCallbacks = [];     // 用於存放全部then方法成功態的回調
	    this.onRejectedCallbacks = [];      // 用於存放全部then方法失敗態的回調
	
	    // 成功 
	    function resolve(value){
	        // 若是value是個Promise實例, 就要先處理該promise實例
	        /*
	        let p = new Promise((resolve, reject)=>{
	            resolve(new Promise(function(resolve, reject){
	                resolve('成功');
	            }));
	        })
	        */
	
	        if(value instanceof Promise){
	            return value.then((data)=>{
	                resolve(data);
	            },(y)=>{
	                reject(y);
	            });
	        }
	
	
	        if(self.status === 'pending'){
	            self.value  = value;
	            self.status = 'fulfilled';
	            self.onFulfilledCallbacks.forEach(fn=>fn());
	        }  
	    }
	
	    function reject(reason){
	        if(self.status === 'pending'){
	            self.reason = reason;
	            self.status = 'rejected';
	            self.onRejectedCallbacks.forEach(fn=>fn());
	        }
	    }
	
	    try {
	        executor(resolve, reject);  // 該函數會在new Promise()實例時當即調用,它接收兩個參數: resolve reject
	    }catch(err){
	        reject(err);
	    }
	    
	}
	
	
	function resolvePromise(x, promise2, resolve, reject){
	    // 若是then的回調函數中返回以前的promsie,就有問題了(由於狀態一旦改變就被凍結,不能再次變化))
	    if(x === promise2){
	        return reject(new TypeError('循環應用'));
	    } 
	
	    // 判斷x是普通值仍是對象,若是是普通值,直接resolve
	    // 若是是對象或函數執行
	    if((x !== null && typeof x === 'object') || typeof x === 'function'){
	        // 這裏能夠是promise,而後嘗試執行
	        try {
	            // 判斷有沒有then方法, 在獲取then的過程當中也可能會出錯(好比某個對象的then屬性get的時候拋出錯誤)
	            /*
	            let obj = {};
	            Object.defineProperty(obj, 'then', {
	                get(){
	                    throw new Error('不讓你get!');
	                }
	            });
	            */
	            let then = x.then;   // 獲取x的then屬性; 若是沒有就會拋出錯誤
	            if(typeof then === 'function'){
	                then.call(x, (y)=>{
	                    // y有可能也是一個promise
	                    // 遞歸解析,直到結果是普通值爲止
	                    resolvePromise(y, promise2, resolve, reject);
	                }, (r)=>{
	                    reject(r);
	                });
	            } else {  
	                // 有多是普通對象或普通值 {then: 'xbs'}或{then: {}}
	                resolve(x);
	            }
	        }catch(e){
	            // 沒有then那可能就是一個普通對象{a:xxx}
	            reject(e);
	        }
	    } else {
	        resolve(x);
	    }
	    
	}
	
	Promise.prototype.then = function(onFulfilled, onRejected){
	    // .then().then().then()值的穿透, 由於咱們在then沒傳回調參數時,手動給其添加了相應的回調函數
	    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=> value;
	    onRejected = typeof onRejected === 'function' ? onRejected: err => {throw err};
	    let self = this;
	    let promise2 = new Promise(function(resolve, reject){
	        // 該executor函數會馬上執行,所以把以前的狀態判斷及成功回調的代碼移到此處與以前功能同樣;
	        // 須要說明的是這個promise須要根據上一個的狀態和值進行判斷,故而設置self變量用於引用上一個this
	        if(self.status === 'fulfilled'){
	            // 這裏要使用promise2, 因此須要增異步保證能夠獲取到promise2;
	            // 爲何要使用promise2? - 由於每次then要返回的是一個新的promise, 若是有人要返回上一個promise呢, 這時候就須要去判斷,promise2和x的關係
	            /*
	                // 看這裏就知道爲何要判斷x與promise2的關係了
	                let p = new Promise((resolve,reject)=>{
	                    resolve('成功');
	                })
	                p.then((value)=>{
	                    return p;
	                }); 
	            */
	
	            // 使用定時器是爲了保障promise2能獲取到; (先執行同步代碼)異步代碼(setTimeout是宏任務,主棧代碼執行完畢,微任務執行後再執行)是在同步執行完成後執行,故而能夠獲取到promise2
	            setTimeout(function(){   
	                // 爲何要try呢? 由於沒人能保證櫻花大道上沒有狗狗的翔 
	                /*
	                    let p = new Promise((resolve,reject)=>{
	                        resolve('成功');
	                    })
	                    p.then((value)=>{
	                        throw new Error('就欺負你怎麼了');  // 成功態中拋錯誤,可謂防不勝防啊
	                    }, (reason)=>{
	                        // 失敗回調...
	                    })
	                */
	                try {
	                    let x = onFulfilled(self.value); 
	                    // 若是這次x是返回的新的promise如何處理? 
	                    // 採用統一的方法來處理x,判斷x是promise仍是普通值
	                    resolvePromise(x, promise2, resolve, reject);
	                } catch(err) {
	                    // 若是執行函數時拋出失敗 那麼會走向下一個then的失敗狀態
	                    reject(err);
	                }                        
	            }, 0);
	        }
	    
	        if(self.status === 'rejected'){
	            setTimeout(function(){
	                try {
	                    let x = onRejected(self.reason);  // onRejected處理
	                    resolvePromise(x, promise2, resolve, reject);
	                } catch(err) {
	                    // 若是執行函數時拋出失敗 那麼會走向下一個then的失敗狀態
	                    reject(err);
	                }  
	            }, 0)
	        }
	    
	        if(self.status === 'pending'){
	            // 異步的處理在這裏
	            // 由於須要待異步執行完成後調用執行,而什麼時候調用並不知道; 所以要先存起來(訂閱),待狀態改變再執行(發佈)
	            self.onFulfilledCallbacks.push(()=>{
	                setTimeout(()=>{
	                    // 一樣也會遇到成功態回調裏面拋出錯誤的狀況,因此也要try{}catch(){}一下
	                    try{
	                        let x = onFulfilled(self.value);
	                        resolve(x); 
	                    }catch(err){
	                        reject(err);
	                    }
	                },0);
	            });
	            self.onRejectedCallbacks.push(()=>{
	                setTimeout(()=>{
	                    // 一樣也會遇到成功態回調裏面拋出錯誤的狀況,因此也要try{}catch(){}一下
	                    try{
	                            let x = onRejected(self.reason);
	                            reject(x);
	                        }catch(err){
	                            reject(err);
	                        }
	                    },0);
	            });
	        }
	    });
	    return promise2;
	};

複製代碼
  • 解析

其實到這裏,Promise的核心已經實現了


Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數

  • 使用
getJSON('/posts.json').then(function(posts) {
	  // ...
	}).catch(function(error) {
	  // 處理 getJSON 和 前一個回調函數運行時發生的錯誤
	  console.log('發生錯誤!', error);
	});
複製代碼
  • 源碼
Promise.prototype.catch = function(errCallback){
	    return this.then(null, errCallback);
	};

複製代碼

Promise.all

Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例

  • 使用
let p1 = new Promise((resolve, reject)=>{
	    resolve('p1-success!');
	});
	
	let p2 = new Promise((resolve, reject)=>{
	    setTimeout(()=>{
	        resolve('p2-success!');
	    }, 2000);
	});
	
	let p3 = new Promise((resolve, reject)=>{
	    setTimeout(()=>{
	        resolve('p3-success!');
	    }, 3000);
	});
	
	let p = Promise.all([p1, p2, p3]).then((result)=>{
	    console.log(result);	// [ 'p1-success!', 'p2-success!', 'p3-success!' ]
	});

複製代碼
  • 源碼
Promise.all = function(values){
	    return new Promise(function(resolve, reject){
	        let results = [];	// 存放結果
	        let index = 0;		// 處理方法執行了幾回
	        function processData(resIndex, data){
	            index++;
	            results[resIndex] = data;		// 將執行結束後的結果存放到結果數組(由於結果和執行順序有嚴格對應關係,因此不能用push,用arr[0] = value的形式);
	            if(index === values.length){
	                resolve(results); 		// 當結果數組和執行操做的數量同樣時,將結果返回
	            }
	        }
	        
	        for(let i = 0; i < values.length; i++){
	            let current = values[i];
	            if(current && current.then && typeof current.then === 'function'){
	                // promise
	                current.then(y=>{
	                    processData(i, y);
	                },reject)
	            } else {
	                processData(i, current); // 若是是普通值,直接返回
	            }
	        }
	    });
	};

複製代碼
  • 解析

    1. Promise.all的實現原理的核心是計數器

Promise.race

  • 使用
let p1 = new Promise((resolve, reject)=>{
	    resolve('p1-success!');
	});
	
	let p2 = new Promise((resolve, reject)=>{
	    setTimeout(()=>{
	        resolve('p2-success!');
	    }, 2000);
	});
	
	let p3 = new Promise((resolve, reject)=>{
	    setTimeout(()=>{
	        resolve('p3-success!');
	    }, 3000);
	});
	
	let p = Promise.race([p1, p2, p3]).then((result)=>{
	    console.log(result); //'p1-success!'
	});

複製代碼
  • 源碼
Promise.race = function(values){
	    return new Promise((resolve, reject)=>{
	        for(let i = 0; i < values.length; i++){
				let current = values[i];
	            if(current && current.then && typeof current.then === 'function'){
	                current.then(resolve,reject);
	            } else {
	                resolve(current);
	            }
	        }
	    });
	};

複製代碼

Promise.resolve()

Promise.resolve 能夠將現有對象轉換爲Promise對象

  • 使用
let p = Promise.resolve(300);
	console.log(p instanceof Promise); // true
複製代碼
  • 源碼
Promise.resolve = function(value){
	    return new Promise(function(resolve,reject){
	        resolve(value);
	    });
	};
複製代碼

Promise.reject()

Promise.reject 能夠將現有對象轉換爲Promise對象

  • 使用
let p = Promise.reject(300);
	console.log(p instanceof Promise); // true
複製代碼
  • 源碼
Promise.reject = function(reason){
	    return new Promise(function(resolve,reject){
	        reject(reason);
	    });
	};
複製代碼

結語

先告一段落啦, 因理解能力有限, 不免會有遺漏和誤差,若是您發現了請告知! 學習和成長的路上看了不少大佬的博客\視頻\文檔\代碼,一直在消費大佬的辛苦成果,本身寫一寫,算是向大佬致敬了!

相關文章
相關標籤/搜索