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的特点:
- 对象的状态是不受外界改变的,Promise对象代表一个异步操作,有三种状态:
pending(进行中)
、fulfilled(已成功)
、rejected(已失败)
。只有异步操作的结果,才能决定是哪个状态,任何其它操作都无法改变 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。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