jsvascript之promise

为什么会有Promise?

我们通常都会说为了解决回调地狱。

那好,什么是回调地狱:

多层嵌套的问题。 每种任务的处理结果存在两种可能性(成功或失败),那么需要在每种任务执行结束后分别处理这两种可能性。

怎么实现一个Promise?

智者见者,仁者见仁,不同的人就会有不同的Promise实现,但是大家都必须遵循promise a+ 规范 ,那符合规范的一个Promise到底是长什么样的?

  1. Promise是一个类, 类中需要传入一个executor执行器, 默认会立即执行,就像下面这样会立即打印出1
new Promise(() => {
	console.log(1);
});
  1. promise有内部会提供两个方法,注意不是原型对象上的,这两个方法会传给用户,可以更改promise的状态。
  2. promise有三个状态:
    等待(PENDING)
    成功(RESOLVED)返回成功的结果,如果不写结果返回undefined
    失败(REJECTED)返回失败的原因,如果不写原因返回undefined

promise只会从等待变为成功或者从等待变为失败。
每个promise实例上都要有一个then方法, 分别是成功和失败的回调。

ok,基于以上所述我们写一个最基本的promise

const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';

class Promise {
	constructor(executor) {
		this.status = PENDING; // 宏变量, 默认是等待态
		this.value = undefined; // then方法要访问到所以放到this上
		this.reason = undefined; // then方法要访问到所以放到this上
		let resolve = (value) => {
			if (this.status === PENDING) {// 保证只有状态是等待态的时候才能更改状态
				this.value = value;
				this.status = RESOLVED;
			}
		};
		let reject = (reason) => {
			if (this.status === PENDING) {
				this.reason = reason;
				this.status = REJECTED;
			}
		};
		// 执行executor传入我们定义的成功和失败函数:把内部的resolve和reject传入executor中用户写的resolve, reject
		try {
			executor(resolve, reject);
		} catch(e) {
			console.log('catch错误', e);
			reject(e); //如果内部出错 直接将error手动调用reject向下传递
		}
	}
	then(onfulfilled, onrejected) {
		if (this.status === RESOLVED) {
			onfulfilled(this.value);
		}
		if (this.status === REJECTED) {
			onrejected(this.reason);
		}
	}
}
module.exports = Promise;

我们平时使用promise基本上都是把一些请求接口的逻辑封装在一个promise内,当请求成功后把数据resolve出去,或者请求失败之后把错误reject出去,也就是说promise必须支持异步,那我们想一想如何支持异步呢?
答案就是发布订阅者模式,看代码实现吧

const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';

class Promise {
	constructor(executor) {
		this.status = PENDING; // 宏变量, 默认是等待态
		this.value = undefined; // then方法要访问到所以放到this上
		this.reason = undefined; // then方法要访问到所以放到this上
		this.onResolvedCallbacks = [];// 专门存放成功的回调函数
		this.onRejectedCallbacks = [];// 专门存放成功的回调函数
		let resolve = (value) => {
			if (this.status === PENDING) {// 保证只有状态是等待态的时候才能更改状态
				this.value = value;
				this.status = RESOLVED;
				// 需要让成功的方法依次执行
				this.onResolvedCallbacks.forEach(fn => fn());
			}
		};
		let reject = (reason) => {
			if (this.status === PENDING) {
				this.reason = reason;
				this.status = REJECTED;
				// 需要让失败的方法依次执行
				this.onRejectedCallbacks.forEach(fn => fn());
			}
		};
		// 执行executor传入我们定义的成功和失败函数:把内部的resolve和reject传入executor中用户写的resolve, reject
		try {
			executor(resolve, reject);
		} catch(e) {
			console.log('catch错误', e);
			reject(e); //如果内部出错 直接将error手动调用reject向下传递
		}
	}
	then(onfulfilled, onrejected) {
		if (this.status === RESOLVED) {
			onfulfilled(this.value);
		}
		if (this.status === REJECTED) {
			onrejected(this.reason);
		}
		// 处理异步的情况
		if (this.status === PENDING) {
			// this.onResolvedCallbacks.push(onfulfilled); 这种写法可以换成下面的写法,多包了一层,这叫面向切片编程,可以加上自己的逻辑
			this.onResolvedCallbacks.push(() => {
				// TODO ... 自己的逻辑
				onfulfilled(this.value);
			});
			this.onRejectedCallbacks.push(() => {
				// TODO ... 自己的逻辑
				onrejected(this.reason);
			});
		}
	}
}
module.exports = Promise;

写点测试代码试试看吧

let promise = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve('xxx');
	}, 1000);
});
// 发布订阅模式应对异步 支持一个promise可以then多次
promise.then((res) => { 
	console.log('成功的结果1', res);
}, (error) => { 
	console.log(error);
});

promise.then((res) => { 
	console.log('成功的结果2', res);
}, (error) => { 
	console.log(error);
});

结果

成功的结果1 xxx
成功的结果2 xxx

到此,我们其实做了很少的工作但已经实现了promise最基本也是最核心的功能了。接下来我们加上链式调用,这里面可能比较绕,但只要我们记住下面几条就会很轻松掌握其中原理:

  1. then方法返回的必须是一个promise,这样才能保证链式调用。
  2. 如果then内部的回调函数执行结果依然是一个promise那就把这个promise的结果resolve出去。
  3. 任何一个promise必须是resolve之后才能走到它then方法,从而创建下一个的promise。
  4. 什么时候走成功回调?then中返回一个普通值或者一个成功的promise
  5. 什么时候走失败回调?返回一个失败的promise,或者抛出异常

接下来看看代码理解下吧


const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';

function resolvePromise(promise2, x, resolve, reject) {
	if((typeof x === 'object' && x != null) || typeof x === 'function') {
		// 有可能是promise, 如果是promise那就要有then方法
		let then = x.then;
		if (typeof then === 'function') { // 到了这里就只能认为他是promise了
			// 如果x是一个promise那么在new的时候executor就立即执行了,就会执行他的resolve,那么数据就会传递到他的then中
			then.call(x, y => {// 当前promise解析出来的结果可能还是一个promise, 直到解析到他是一个普通值
				resolvePromise(promise2, y, resolve, reject);// resolve, reject都是promise2的
			}, r => {
				reject(r);
			});
		} else {
			// 出现像这种结果 {a: 1, then: 1} 
			resolve(x);
		}
	} else {
		resolve(x);
	}
}

class Promise {
	constructor(executor) {
		this.status = PENDING; // 宏变量, 默认是等待态
		this.value = undefined; // then方法要访问到所以放到this上
		this.reason = undefined; // then方法要访问到所以放到this上
		// 专门存放成功的回调函数
		this.onResolvedCallbacks = [];
		// 专门存放成功的回调函数
		this.onRejectedCallbacks = [];
		let resolve = (value) => {
			if (this.status === PENDING) { // 保证只有状态是等待态的时候才能更改状态
				this.value = value;
				this.status = RESOLVED;
				// 需要让成功的方法一次执行
				this.onResolvedCallbacks.forEach(fn => fn());
			}
		};
		let reject = (reason) => {
			if (this.status === PENDING) {
				this.reason = reason;
				this.status = REJECTED;
				// 需要让失败的方法一次执行
				this.onRejectedCallbacks.forEach(fn => fn());
			}
		};
		// 执行executor 传入成功和失败:把内部的resolve和 reject传入executor中用户写的resolve, reject
		try {
			executor(resolve, reject); // 立即执行
		} catch (e) {
			console.log('catch错误', e);
			reject(e); //如果内部出错 直接将error 手动调用reject向下传递
		}
	}
	then(onfulfilled, onrejected) {
		// 为了实现链式调用,创建一个新的promise
		let promise2 = new Promise((resolve, reject) => {
			if (this.status === RESOLVED) {
				// 执行then中的方法 可能返回的是一个普通值,也可能是一个promise,如果是promise的话,需要让这个promise执行
				// 使用宏任务把代码放在一下次执行,这样就可以取到promise2,为什么要取到promise2? 这里在之后会介绍到
				setTimeout(() => {
					try {
						let x = onfulfilled(this.value);
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) { // 一旦执行then方法报错就走到下一个then的失败方法中
						console.log(e);
						reject(e);
					}
				}, 0);
			}
			if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = onrejected(this.reason);
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			}
			// 处理异步的情况
			if (this.status === PENDING) {
				// 这时候executor肯定是有异步逻辑
				this.onResolvedCallbacks.push(() => {
					setTimeout(() => {
						try {
							let x = onfulfilled(this.value);
							// 注意这里传入的是promise2的resolve和reject
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
				this.onRejectedCallbacks.push(() => {
					setTimeout(() => {
						try {
							let x = onrejected(this.reason);
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
			}
		});

		return promise2;
	}
}

module.exports = Promise;

主要就是多了resolvePromise这么一个函数,用来递归处理then内部回调函数执行后的结果,它有4个参数:

  1. promise2: 就是新生成的promise,这里至于为什么要把promise2传过来后面会介绍。
  2. x: 我们要处理的目标
  3. resolve: promise2的resolve, 执行之后promise2的状态就变为成功了,就可以在它的then方法的成功回调中拿到最终结果。
  4. reject: promise2的reject, 执行之后promise2的状态就变为失败,在它的then方法的失败回调中拿到失败原因。

到了这里基本上完整的Promise已经实现了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScriptPromise是一种用于处理异步操作的机制。它可以让我们更优雅地处理回调函数嵌套的问题,使得异步操作的代码更易于阅读和维护。 一个Promise对象代表一个异步操作的最终完成或失败,并且它的状态只能从“未完成”转变为“完成”或“失败”。在完成或失败时,Promise对象会传递一个结果或原因。 使用Promise对象的步骤如下: 1. 创建一个Promise对象,传入一个执行器函数(executor function),该函数接收两个参数:resolve和reject。 2. 在执行器函数中,执行异步操作,并在异步操作完成或失败时调用resolve或reject函数。 3. 调用Promise对象的then方法,传入两个回调函数:一个用于处理成功结果,一个用于处理失败原因。 4. 在then方法中,处理成功结果或失败原因。 例如,下面是一个使用Promise对象的例子: ```javascript function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { const data = { name: 'John Doe', age: 30 }; resolve(data); }, 2000); }); } fetchData() .then(data => { console.log(data.name); // 输出 John Doe }) .catch(error => { console.error(error); }); ``` 在上面的例子中,我们使用了Promise对象来模拟一个异步操作,该操作将在2秒后返回一个包含姓名和年龄的对象。然后,我们通过调用then方法传入一个处理成功结果的回调函数,打印出该对象的姓名属性。如果异步操作失败,我们通过调用catch方法传入一个处理失败原因的回调函数,输出错误信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值