js Promise简介 实现

Promise

Promise的完整实现

一个围绕状态展开的Promise

  • Promise根据resolve/reject来改变状态及数据,且只能调用一次
  • Promise内部有2种情况,异步/同步调用
    1. Promise在异步情况下在 resolve/reject 函数中调用then中注册的函数
    1. 同步情况下在queueMicrotask中调用
  • then用于注册回调函数及返回一个新Promise
  • 若then中若没有对应状态的回调函数,则将复制自身状态及数据到新Promise

开始

  • Promise接受一个函数,在构造函数中立即调用
	let p = new Promise((resolve,reject)=>{
		console.log('立即执行')
	});

resolve, reject 2个函数

  • 这两个函数用于传递数据以及对象状态的改变
  • 两种状态 :
    1. resolve调用:对象的状态变成fulfilled
    1. reject调用:对象的状态变成rejected
    1. resolve/reject 不仅改变对象状态,还用于存放数据
    1. 且只能调用一次,因为对象的状态只能改变一次
  • 两个函数对于状态的改变是互斥的
	/*
		两个函数任何一个被调用后, 另一个将不会被调用
		1.
			resolve() 
			reject()	//无效
		2.
			reject()
			resolve()	//无效
		3. 
			reject()
			reject()	//无效
		
	*/


	let p = new Promise((resolve,reject)=>{
		resolve('成功') 
		reject('失败')		//无效
	});

对象的3个状态 pendding , fulfilled , rejected

  • 一开始对象的状态为pending
  • 状态由 pending -> fulfilled 或 pendding -> rejected
  • resolve/reject传递的数据存放在对象的[[PromiseResult]]中
  • 对象的状态在[[PromiseState]]
	let p = new Promise((resolve,reject)=>{
		setTimeout(() => {
			resolve('成功')     
		}, 0);
	});

	/*
		[[PromiseState]]: 'pending', 
		[[PromiseResult]]: undefined
	*/
	console.log(p);

	/*
		[[PromiseState]]: 'fulfilled', [[PromiseResult]]: '成功'
	*/
	setTimeout(() => {
		console.log(p);
	}, 10);

then函数

  • 通过上面所知,状态一旦改变,即可获取传递来的数据
  • 当状态改变意味调用了 resolve / reject,随即会调用then中注册的函数
  • then 有2个参数,第一个在fulfilled状态时执行, 第二个在rejected状态时执行
    1. 对象的状态为fulfilled时调用第1个回调
    1. 对象的状态为rejected时调用第2个回调
  • 注意,Promise的resolve,reject由JS本身提供,then的两个回调函数由我们自己提供
	/*
		由于调用了reject, 因此对象状态从pending -> rejected
	*/
	
	let p = new Promise((resolve,reject)=>{
		setTimeout(() => {

			// p 从 pending转到rejected
			reject('失败')     
		}, 0);
	});

	//提供了2个回调函数, rejected状态将调用第二个函数
	p.then(res=>{
		console.log("success:",res)
	}, res=>{

		//state: Promise [[PromiseState]]:
		 'rejected', [[PromiseResult]]: '失败'
		console.log("state:",p) 
	});
	
	/*
		以下两者等价
		p.then(null,res=>{
					console.log('failed:',res)
				})
				
		//语法糖
		p.catch(res=>{
			console.log('failed:',res)
		})
	*/

then临时小总结

  • 当Promise对象调用 resolve / reject时,将设置自身状态及数据
  • 同时将在resolve/reject中根据自身状态来调用 then 中注册的回调函数,并把数据传递给回调函数

then的连续调用

  • 同一个Promise对象.then 可连续调用
  • Promise对象维护着一个数组
    1. 异步情况,每次在then中注册函数,就往数组中push
    1. 同步情况,将函数调用放入queueMicrotask中调用
	let p = new Promise((resolve,reject)=>{
		resolve('aaaa') 	//fulfilled状态
	});

	p.then(res=>{
		console.log('success:',res) // 第一次调用
	},res=>{
		console.log('failed:',res)	//不会调用
	})
	p.then(res=>{
		console.log('第二次')	// 还会调用
	})
	
	//p.then()p.then()p.then() 都将依次调用

then 返回一个新的Promise对象

  • then / catch / finally 都将return new Promise(…)
	let p = new Promise((resolve,reject)=>{

		// p 将从pending转到fulfilled
		resolve('aaaa') 
	});

	//注册回调函数
	let ret = p.then(res=>{
		console.log('success:',res)
		/*
			不写 return , 默认:
			return new Promise((resolve,reject)=>{
				resolve(undefined);
			})
			
			如果写了 return '1111':
			return new Promise((resolve,reject)=>{
				resolve('1111');
			})		
		*/
	},res=>{
		console.log('failed:',res)
	})
	setTimeout(() => {
		/*
			[[PromiseState]]: 'fulfilled', [[PromiseResult]]: undefined
		*/
		console.log('ret:',ret)    
	}, 10);

返回的新对象的数据将是回调函数的返回值

  • 新对象的数据将获取回调函数的返回值,并作为自己的数据
  • 具体实现过程是 新对象.resolve( then中的回调() );
	let p = new Promise((resolve,reject)=>{

		// p.[[PromiseState]]会变成'rejected'
		setTimeout(() => {
			reject('失败')     
			}, 0);
	});

	let p1 = p.then(res=>{
		console.log("success:",res)	//不会执行
	}, res=>{
		console.log("failed:",res)

		/*
		 new Promise((resolve,reject)=>{
            resolve(123);  
		});
		*/
		return 123;
	});
	setTimeout(() => {
        //[[PromiseState]]: 'rejected', [[PromiseResult]]: '失败'
		console.log('p:',p)
        
        //[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 123
		console.log('p1:',p1)    
	}, 10);

* then 返回的新对象,其状态及数据

  • then中返回的新对象其状态及数据:
    1. Promise对象在调用resolve/reject会调用then中注册过的回调函数
    1. 有当前对象状态对应的回调函数,则返回fulfilled状态新对象
    1. 如没有,则将传递自身数据及状态给新对象
	
	let p = new Promise((resolve,reject)=>{

		// p 从pending 变成 rejected
		setTimeout(() => {
			reject('失败')     
		}, 0);
	});

	/*
		当p在调用reject时会调用then中注册过的函数
		当前没有 rejected 状态对应的函数
		因此 p 的状态和数据将传递给 p1
	*/
	let p1 = p.then(res=>{
		console.log("success:",res)
	});

	// p1 注册了 rejected 状态该使用的回调
	// p1 返回的新对象将是一个 fulfilled的状态
	let p2 = p1.catch(res=>{
		console.log('catch:',res)
	})

	setTimeout(() => {
		// [[PromiseState]]: 'rejected', [[PromiseResult]]: '失败'
		console.log('p:',p)

		//[[PromiseState]]: 'rejected', [[PromiseResult]]: '失败'
		console.log('p1:',p1)    

		//[[PromiseState]]: 'fulfilled', [[PromiseResult]]: undefined,
		console.log('p2:',p2)    
	}, 100);


  • 再看一个例子
  • 下面的then中全部没有对应的回调函数,因此对象的状态和数据都将一次次传递给新对象
	
	let p = new Promise((resolve,reject)=>{

		// p 从pending 将转到 fulfilled
		setTimeout(() => {
			resolve('失败')     
		}, 0);
	});

	// 由于p.then没有对应函数,因此p的状态及结果都将复制给p1
	let p1 = p.then(null);

	// p1只处理了出错的情况,没处理成功的情况,因此p2新对象也将复制p1的状态和结果
	let p2 = p1.catch(res=>{
		console.log('catch:',res)
	})

	setTimeout(() => {
		// PromiseState: 'fulfilled', [[PromiseResult]]: '失败',
		console.log('p:',p)

		// PromiseState: 'fulfilled', [[PromiseResult]]: '失败',
		console.log('p1:',p1)    

		// PromiseState: 'fulfilled', [[PromiseResult]]: '失败',
		console.log('p2:',p2)    
	}, 100);


then 小结

  • 在then注册的回调函数,无论同步还是异步都将返回一个新Promise
  • 新对象的状态及数据根据当前对象是否有回调函数来决定
    1. 如果有,则返回fulfilled状态,数据undefined的新对象
    1. 如果没有,将传递自身的状态及数据给新对象,让新对象去处理

最后一个then小例子

  • 结合上面内容,分别观察每个对象的状态
  • 尝试链式调用
	
	let p = new Promise((resolve,reject)=>{

		// p 将由pending 转成rejected
		reject('我在哪里');
	});

	/*
	 第一个then:
	 	p未处理rejected函数,返回新Promise复制了p的状态及数据
	 第二个then:
	 	新Promise对象的状态是rejected
		在then中提供了函数,因此调用此函数,
		同时返回了一个fulfilled状态的新对象,新对象的数据为回调返回值
	第三个then:
		状态为fulfilled的对象在then中有对应函数,
		此对象的数据为前一个返回值
	*/
	p.then(res=>{
		console.log('第一个 then 的success函数 ', res);
	})
	.then(null,res=>{
		console.log('相当于catch ', res);
		return '我成功了'
	})
	.then(res=>{
		console.log('输出了什么 :' , res)
	})

	/*
		相当于catch  我在哪里

		输出了什么 : 我成功了
	*/

自定义返回, 异常

  • 根据上面代码所知
  • 可以返回一个reject对象或者抛出异常,异常也是reject对象
	
	let p = new Promise((resolve,reject)=>{

		// p 由 pending转为 rejected
		reject('我在哪里');
	});

	//p 有对应的回调函数
	let p1 = p.catch(res=>{
		console.log('catch :' , res)

		throw '哈哈哈哈'

		/*
			throw 相当于 
			return Promise.reject('哈哈哈哈');
		*/
		
	})

	//[[PromiseState]]: 'rejected', [[PromiseResult]]: '哈哈哈哈'
	p1.catch(res=>{
		console.log(p1);
	})
  • 把上面的代码更改下
	let p = new Promise((resolve,reject)=>{
		resolve('我在哪里');
	});

	let p1 = p.then(res=>{
		/*
			相当于
			return Promise.reject('哈哈哈哈');
		*/
		throw '哈哈哈哈'
	})

	//与上面代码一致
	p1.catch(res=>{
		console.log(p1);
	})
  • 自定义异常的链式调用
  • 看完上面代码,应该对下面的代码完全了解在做什么
	let p = new Promise((resolve,reject)=>{
		resolve('我在哪里');
	});

	// p转为fulfilled,调用第一个函数

	p.then(res=>{
		console.log('resolve :' , res)
		return Promise.reject("哈哈哈哈")
	})
	.then(()=>{

		/*
			新对象为rejected,then中没有对应回调,此函数将被忽略
			同时返回一个新对象,复制当前对象的rejcted状态及数据
		*/

		console.log('略过此,并返回一个新Promise状态为reject')
		return 123;	
	})
	.then(null,res=>{
		
		/*
			当前对象的状态及数据从上一个对象复制得来,
			当前对象的then有对应回调.
			因此这部分被执行
			此对象返回的新对象将是一个fulfilled状态,数据为当前的返回值
		*/

		console.log('我将被调用:',res)
		return 456;
	})
	.then(res=>{
		
		//当前对象是一个fulfilled状态,有对应回调,因此执行
		console.log('最终在这里:',res)
	})

  • 把上面的代码做一点改变,整体完全一致
	let p = new Promise((resolve,reject)=>{
		resolve('我在哪里');
	});

	p.then(res=>{
		return new Promise((s,r)=>{

			// 返回的新对象状态为 rejected,数据为'哈哈哈哈'
			setTimeout(() => {
				r('哈哈哈哈')    
			}, 1000);
		});
	})
	.then(()=>{
		//不会执行, 返回一个新对象将复制当前的状态及数据
		console.log('略过此,并返回一个新Promise状态为reject')
		return 123;
	})
	.then(null,res=>{
		/*
			有对应回调
			执行并返回一个 fulfilled状态,数据为456的新对象
		*/

		console.log('我将被调用:',res)
		return new Promise((s,r)=>{
			setTimeout(() => {
				s(456);
			}, 500);
		});
	})
	.then(res=>{
		console.log('最终在这里:',res)
	})

catch,finally,resolve,reject,all,allSettled,any,race的具体实现可看Promise实现

Promise的完整实现

你可能感兴趣的:(js,javascript,js,Promise,Promise)