Promise的原理、一步一步手写Promise

Promise的原理、一步一步手写Promise

Promise是干做什么的?

手写我们来看下面这段代码:

function getData(count) {
    get(`/sampleget?count=${count}`, data => {
        console.log(data);
        get(`/sampleget?count=${count}`, data => {
            console.log(data);
            get(`/sampleget?count=${count}`, data => {
                console.log(data);
            });
        });
    });
}
      上面这段代码,就是经典的回调地狱的问题,因为我们是层层嵌套调用函数,耦合性比较强,代码维护起来难度大,所以,Promise就出现了

在ES6中规范中,是对ES6这样定义的:
      Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

      所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise的特点:

  1. 对象的状态是不受外界改变的,Promise对象代表一个异步操作,有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)。只有异步操作的结果,才能决定是哪个状态,任何其它操作都无法改变
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
    在这里插入图片描述
简单的使用

index.js

const show = function(){
	return new Promise((resolve, reject) => {
		resolve(100)
		// reject(10000)
	})
} 

show().then(res=>{
	console.log(res)			// 100
}, err=>{
	console.log(err)
})

这段代码是最基本的Promise的同步调用,使用的是浏览器自带的Promise,那么我们来看看如何实现一个自己的Promise,来达到当前这么一个简单的调用。(在这里我使用的ES6的语法来模拟实现的。)

第一版本代码

Promise.js

	class Promise {
		constructor(exector) {
		    const resolve = value => {
				console.log(value)
			}
			const reject = reason => {
				console.log(reason)
			}
			exector(resolve, reject)		// 传入的函数参数,调用哪个自定义的状态函数
		}
		then(){
			
		}
}

module.exports = Promise

首先定义一个 Promise类,在constructor中调用传入的参数方法,也就是我们需要自定义resolve状态的方法以及reject状态的方法,同时在调用Promise的时候,会调用then的回调函数方法,所以我们也要有一个then方法,最后我们用module.exports把他抛出去,抛出去之后,记得在index.js中的顶部用require引入进来 :

const Promise = require('./promise')

现在我们调用了这个自定义的Promise之后,就不会报错了,接下来我们来完善一下和这个Promise

第二版本代码

class Promise {
	constructor(exector) {
		this.state = 'pending';				//状态
		this.value = undefined;				// 成功的值
		this.reason = undefined;			// 失败的值
	    const resolve = value => {
			if(this.state === 'pending'){		// 保证必须在pending的时候才能调用这个resolve方法
				this.state = 'fulfilled';	// 改变状态
				this.value = value;			// 保存用户传来的值
			}
		}
		const reject = reason => {
			if(this.state === 'pending'){
				this.state = 'rejected';
				this.reason = reason;
			}
		}
		exector(resolve, reject)		// 传入的参数,调用哪个自定义的函数
	}
	then(onFulfilled, onRejected){
		if(this.state === 'fulfilled'){		// 只有当成功的状态才会调用
			onFulfilled(this.value)
		}
		if(this.state === 'rejected'){		// 只有当失败的状态才会调用
			onRejected(this.reason)
		}
	}
}


module.exports = Promise

好了,现在我们已经完成了一个最基本的Promise的实现,虽然能用,但是我们只是完成了一个简单的同步调用,而真正的Promise是用来解决异步调用的,我们在index.js中加入setTimeout形成异步回调的情况,我们就会发现之前写的Promise代码不能使用了。那这是为啥呢?

解析:
      在我们使用new Promise的时候,由于resolve方法在setTimeout当中,所以进入了下一轮的事件循环机制(eventLoop)当中,由于我们调用的then是同步任务之后的微任务,所以会先执行then中的方法,再执行最后的宏任务(setTimeout)。

      但是我们来看当前then方法中,我们并没有对pending状态做处理,所以,是不会输出内容的,到了这里,我们可以使用一种发布订阅设计模式来对这种异步做处理。

第三版本代码

class Promise {
	constructor(exector) {
		this.state = 'pending';
		this.value = undefined;				// 成功的值
		this.reason = undefined;			// 失败的值
		// 成功的发布者数组
		this.onResolvedCallbacks = [];
		// 失败的发布者数组
		this.onRejectedCallbacks = [];
	    const resolve = value => {
			if(this.state === 'pending'){		// 保证必须在pending的时候才能调用这个resolve方法
				this.state = 'fulfilled';	// 改变状态
				this.value = value;			// 保存用户传来的值
				this.onResolvedCallbacks.forEach(fn => fn())		// 订阅者接收了发布者的消息并调用
			}
		}
		const reject = reason => {
			if(this.state === 'pending'){
				this.state = 'rejected';
				this.reason = reason;
				this.onRejectedCallbacks.forEach(fn => fn())
			}
		}
		exector(resolve, reject)		// 传入的参数,调用哪个自定义的函数
	}
	then(onFulfilled, onRejected){
		if(this.state === 'fulfilled'){		// 只有当成功的状态才会调用
			onFulfilled(this.value)
		}
		if(this.state === 'rejected'){		// 只有当失败的状态才会调用
			onRejected(this.reason)
		}
		if(this.state === 'pending'){		// 当异步操作的时候,对pending状态做处理
			this.onResolvedCallbacks.push(()=>{		//存储到成功发布者数组当中
				onFulfilled(this.value)
			})
			this.onRejectedCallbacks.push(()=>{		//存储到失败发布者数组当中
				onRejected(this.reason)
			})
		}
	}
}


module.exports = Promise

在第三个版本当中,我们主要添加了发布订阅者的模式,创建了发布者的成功和失败的数组,调用then方法的时候,我们对pending状态做处理,将回调函数加入到相应的发布者数组当中,等待订阅者的调用。 在执行到setTImeout宏任务时,我们已经将回调函数存储到了发布者数组中,只需要在相对应状态的方法当中订阅(调用)就可以了。

      现在我们的Promise已经完成了异步的操作,接下来我们接着完善Promise的链式调用的操作,首先来看index.js中的代码


const Promise = require('./promise')

const show = function(){
	return new Promise((resolve, reject) => {
		setTimeout(function() {
			resolve(100)
		}, 10);
	})
} 

show().then(res=>{
	console.log(res)
	return '123'
}, err=>{
	console.log(err)
}).then(res=>{
	console.log(`$res'___'`);
}, err=>{
	console.log(err);
})

由于我们自己的Promise没有对链式调用做处理,所以当执行了这段代码后,会报错的,同时链式调用可以将上一个的then的返回值当做下一个then的参数,接下来我们再次对自己的Promise代码进行修改

因为只是对then的方法做出了修改,所以下面只有then的代码

then(onFulfilled, onRejected){
		const promise2 = new Promise((resovle, reject) => {
			if(this.state === 'fulfilled'){		// 只有当成功的状态才会调用
				onFulfilled(this.value)
			}
			if(this.state === 'rejected'){		// 只有当失败的状态才会调用
				onRejected(this.reason)
			}
			if(this.state === 'pending'){		// 当异步操作的时候,对pending状态做处理
				this.onResolvedCallbacks.push(()=>{		//存储到成功发布者数组当中
					onFulfilled(this.value)
				})
				this.onRejectedCallbacks.push(()=>{		//存储到失败发布者数组当中
					onRejected(this.reason)
				})
			}
		})
		return promise2
	}

我们将之前的判断代码都放入到promise当中,就可以实现链式调用了

———————————————————————————————————————————————————————————————————————
打个断点下面的内容之后再更新,我先处理一下的手上的Bug T^T

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值