当前位置: 首页 > news >正文

Promise源码解析-步步为营皆可及

本文根据promise的用法, 带您一步一步的了解promise的源码内幕; 本文采用要点、使用、源码分析的思路,步步为营,一点一点的剖析Promise的神秘世界; 本文Promise源码部分采用es5语法

Promise定义

  • promise是异步解决方案; 字面意思是"承诺",即未来某个时刻才能得到结果;
  • 从语法上来说,它是一个对象,从它可以获取异步操作的消息;

基本用法 - 构造函数

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例

  • 使用:
	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)是两个函数, 这两函数的作用:

  • 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
	}

复制代码
  • 解析:

    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实例添加状态改变时的回调函数;

  • 使用:
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);
	    });
	};
复制代码

结语

先告一段落啦, 因理解能力有限, 难免会有遗漏和偏差,如果您发现了请告知! 学习和成长的路上看了很多大佬的博客\视频\文档\代码,一直在消费大佬的辛苦成果,自己写一写,算是向大佬致敬了!

转载于:https://juejin.im/post/5ca0d0c051882567e32fc459

相关文章:

  • 图像处理之USM锐化
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • POJ 3384 Feng Shui
  • Android环境下hanlp汉字转拼音功能的使用介绍
  • 轻松搭建docker应用的mesos集群
  • Android Studio发布Release版本之坑--Unknown host 'd29vzk4ow07wi7.cloudfront.net'
  • Spring—Quartz定时调度CronTrigger时间配置格式说明与实例
  • 一步步教你用 CSS 为 SVG 添加过滤器
  • js学习笔记之日期倒计时(天,时,分,秒)
  • iOS app和Extension数据共享DB时候遇到的坑 NSFileManager共享数据的坑
  • ASP.NET MVC学习之路由篇(2)
  • 用Go语言写Android应用 (2) - 从Android的Java调用Go代码
  • RootMe--HTTP - Open redirect
  • SerializeDeserialize
  • Unity3dShader边缘发光效果
  • 【笔记】你不知道的JS读书笔记——Promise
  • Javascripit类型转换比较那点事儿,双等号(==)
  • js如何打印object对象
  • use Google search engine
  • webgl (原生)基础入门指南【一】
  • 百度地图API标注+时间轴组件
  • 闭包--闭包之tab栏切换(四)
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 对象管理器(defineProperty)学习笔记
  • 前端知识点整理(待续)
  • 前嗅ForeSpider教程:创建模板
  • 区块链共识机制优缺点对比都是什么
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 小程序01:wepy框架整合iview webapp UI
  • 正则学习笔记
  • 阿里云API、SDK和CLI应用实践方案
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • #、%和$符号在OGNL表达式中经常出现
  • $(function(){})与(function($){....})(jQuery)的区别
  • (09)Hive——CTE 公共表达式
  • (1)(1.13) SiK无线电高级配置(五)
  • (13):Silverlight 2 数据与通信之WebRequest
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (python)数据结构---字典
  • (附源码)计算机毕业设计大学生兼职系统
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (转)socket Aio demo
  • (转)视频码率,帧率和分辨率的联系与区别
  • ./configure,make,make install的作用(转)
  • .net core webapi 大文件上传到wwwroot文件夹
  • .net core 连接数据库,通过数据库生成Modell
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET建议使用的大小写命名原则
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • /bin/rm: 参数列表过长"的解决办法
  • @property括号内属性讲解
  • @vue/cli 3.x+引入jQuery