本文介绍: 这一点是promise存在最大价值体现。它的实现其实就是then方法传入的函数异步操作执行即可这里为了方便描述,我只说成功的情况。一种情况是在调用then方法时候异步操作还未完成promise状态pedding)。大致的流程如下:【这里本来是动图的,但是csdn不允许上传这么大的动图,,,具体的可以看我公众号吧,嘻嘻,我最近开始写公众号啦!撒花!另一种情况是,在调用then方法时候异步操作已经完成(promise状态已经变成fulfilled)。

一,背景

1.1,js的单线程

这一切,要从js诞生之初说起,因为js是单线程语言

js线程原因:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂同步问题比如,假定JavaScript同时有两个线程一个线程在某个DOM节点添加内容,另一个线程删除这个节点,这时浏览器应该以哪个线程为准?当然,这可以加锁解决,但是加锁会变得更复杂,得不偿失。
1.2,同步异步讨论

简单理解同步异步

同步操作可以理解为不花费时间的操作,而异步操作是需要定时间的。

线程意味着所有的任务都只能乖乖排队执行,就和我们排队过地铁闸机一样。但是有的人刷卡,有的人刷码,有的人刷脸,所花费的时间不一样,假如个人手机没网了,就会卡住队伍,造成堵塞。这就是单线程带来的同步阻塞问题

为了解决这个问题,js引入回调函数机制,对于一个IO操作,比如一个ajax,当发出一个异步请求后,程序不会阻塞在那里等待结果返回,而是继续执行下面的代码。当请求成功获取结果后,就会调用回调函数处理后面的事情,这个就是异步阻塞。在js中,异步操作的处理都是异步非阻塞

所以可以说js是单线程异步编程语言

如下代码

console.log(1)
setTimeout(()=>{
    console.log(2)
},100)
console.log(3)
//执行结果:132

setTimeout里面执行的就是异步操作的回调函数等待完成后执行其中的代码。类似于地铁过闸机没网的那个兄弟先站到旁边处理网络问题解决了再排队进闸机,这样就不会堵着后续的人。

1.3,回调函数

那如果异步操作完成我们想要用这个结果做一些事情呢?有人说可以把代码写在异步操作的结果处理里面,但是这样代码一层包一层的,很难复用和维护。

为了解决这个问题,有人提出了回调函数方案

其实上文中的setTimeout里面的箭头函数就是一个回调函数,但是不够直观,这里再举个例子

异步耗时工作常见的有以下这些:

定时器
建立网络连接文件读写数据

可以拿向文件读写数据来举例,先看如下代码

const fs = require('fs')
function printA(){
    console.log(a)
}
function readFile(){
    fs.readFile('test.txt', (err, data) => {
        if (err) {
          console.error(err)
          return
        }
        // data二进制类型需要转换成字符串
        console.log("读取到的文本内容:",data.toString())
        a=1
      })
}
let a=0
readFile()
printA()

从上文可以知道a虽然被修改了,但它是在读文件操作(异步操作)中被修改的,因为js异步非阻塞特性,在执行printA中的打印a时,它并没有执行,所以打印出来的a将是0。

如何把a=1打印出来呢?

我们可以把一个函数作为参数,传给readFile,在readFile函数中执行打印操作。

const fs = require('fs')
function printA(){
    console.log(a)
}
function readFile(callback){
    fs.readFile('test.txt', (err, data) => {
        if (err) {
          console.error(err)
          return
        }
        // data 是二进制类型需要转换字符串
        console.log("读取到的文本内容:",data.toString())
        a=1
        callback()//这就是传进来的回调函数
      })
}
let a=0
readFile(printA)

这就是回调函数:函数体在完成某种操作后由内向外调用某个外部函数,因为是在函数体内部调用,所以能由内向外获取到相关变量作用域链)。

简单点说,就是把函数当作参数传递给异步操作完成之后再执行

1.4,js事件循环机制

上文说到,js的异步非阻塞机制,那它是如何实现就一个主线程,完美运行所有的同步异步代码呢?

这就需要理解下js的事件循环机制,也就是人们所说的(Event Loop),可以用下图
请添加图片描述

常见的微任务

Promises.(then catch finally),process.nextTick, MutationObserver
DOM渲染触发

这里值得注意的是promise内是同步,而.then后才是微任务。

常见的宏任务:

整体代码scriptsetTimeoutsetIntervalsetImmediate,I/O,UI renderingnew 
DOM渲染触发

也就是说,遇到异步函操作,还需要判断是宏任务还是微任务,宏任务的话,就把异步操作的结果加入宏任务队列,微任务的话,就加入到微任务队列。
于是,异步到的队列,就由原来的一个事件队列,变成了宏队列和微队列两个,而主线程空了的话,会先去微队列中查找(若在这个过程中,微队列的事件又产生的新的微任务加入队尾,也会在本次循环中进行处理,简而言是就是把每轮循环把微队列搞空),然后再去宏队列中查找(而每次宏任务只执行一个,就又轮到微任务队列)。

一步主线程执行同步任务的同时,把一些异步任务放入‘任务队列’(task queue)中,等待主线程的调用为空时,再依次从队列出去任务去执行;
第二步检测任务队列中的微队列是否为空,若不为空,则取出一个微任务入栈执行;然后继续执行第2步;如果微队列为空,则开始取出宏队列中的一个宏任务执行;
第三步:执行完宏队列中的一个宏任务后,会继续检测微队列是否为空,如果有新插入的任务,这继续执行第二步;如果微队列为空,则继续执行宏队列中的下一个任务,然后再继续循环执行第三步;

示例

const fs = require('fs')
setTimeout(function callback(){
	console.log('2')//宏任务
    Promise.resolve().then(()=>{
        console.log(6)//宏任务中微任务
    })
}, 0)
setTimeout(function callback(){
	console.log('又一个宏任务')//宏任务
}, 0)
new Promise((resolve, reject) => {
    console.log('3')//同步
    resolve()
})
.then(res => {
    console.log('4');//微任务
    new Promise((resolve, reject) => {
        console.log('8')//同步
        fs.readF ile('test.txt', (err, data) => {
            if (err) {
              console.error(err)
              return
            }
            // data 是二进制类型需要转换字符串
            console.log("读取到的文本内容:",data.toString())
            resolve(data.toString())//宏任务
          })
    })
    .then(res => {
        new Promise((resolve, reject) => {
            console.log('测试')//同步
            resolve()
        })
        .then(res => {
            console.log('再测试');//微任务
        })
    })
})
console.log('5')//同步

打印的值:

3
5
4
8
2
6
又一个宏任务
读取到的文本内容: 测试文本
测试测试
1.5,回调函数的问题和promise的诞生

自此,js已经能够依托事件循环机制,执行所有的任务。但是因为有的任务需要依托异步任务返回结果,所以我们采用了回调函数的解决方案,那如果有多重异步操作,并且最后的操作依赖于这几次异步操作。那么就容易层层嵌套,带来回调地狱的问题。

如下代码,我创建了三个txt文件然后需要依次读取文件内容打印,如果采取回调函数的写法,将是如下样子:

const fs = require('fs')
fs.readFile('test.txt', (err,data) => {
    let reader1=data.toString()
    fs.readFile('test1.txt', (err,data) => {
        let reader2=data.toString()
        fs.readFile('test2.txt', (err,data) => {
            let reader3=data.toString()
            setTimeout(()=>{
                let result=reader1+reader1+reader3
                console.log("获得的结果",result)
            },10)
        })
    })
})

fs模块的readFile的第二个参数,便是一个回调函数,为了按照顺序读取文本内容,则需要多层嵌套,这样只有几次倒还好,实际项目中却容易出现多层,导致代码出现回调地狱,难以阅读维护。

为了解决这个问题,promise应运而生。

回调地狱的产生,原因还是回调函数对结果的处理和异步操作终究还是在一起,并没有分离。而引入promise最大作用,就是把异步操作的过程和结果做到了分离,可以用promise.then()来获取和处理异步操作的结果。使用promise修改上文的代码:

const fs = require('fs')
function readFile(fileName){
    return new Promise((resolve,reject)=>{
        fs.readFile(fileName, (err,data) => {
            resolve(data.toString())
        })
    })
}
let res1,res2,res3
readFile('test.txt')
.then((res)=>{
    res1=res
    return readFile('test1.txt')
})
.then((res)=>{
    res2=res
    return readFile('test2.txt')
})
.then((res)=>{
    res3=res
    console.log("结果",res1+res2+res3)
})

这样一来,就把异步的操作和结果通过.then进行了分离。避免了回调地狱的产生。

二,promise的then方法实现

promise到底是个啥?为何能有如此效果呢?

2.1,promise的三种状态
pending: 一个promise在resolve或者reject前就处于这个状态fulfilled: 一个promise被resolve后就处于fulfilled状态,这个状态不能再改变,而且必须拥有一个不可变的值(value)rejected: 一个promise被reject后就处于rejected状态,这个状态也不能再改变,而且必须拥有一个不可变拒绝原因(reason)
2.2,promise.then方法

一个promise必须拥有一个then方法访问他的值或者拒绝原因then方法两个参数:

promise.then(onFulfilled, onRejected)

并且这两个参数有以下特征

1,onFulfilled 和 onRejected 都是可选参数。若不存在则忽略。
2,onFulfilled在promise结束不可调用结束后必须被调用,其第一个参数为 promise 的终值value,且调用次数不超过一次。
3,onRejected在被拒绝不可被调用,拒绝执行后其必须被调用,其第一个参数为 promise 的原因reason,且调用册数不超过一次

而这个then方法支持以下特征

1,then 方法必须返回一个 promise 对象。这样才支持多次then链式调用。
2,then 方法可以被同一个 promise 调用多次。当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调,当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调。
2.3,先写基本promise,实现异步操作与结果的分离

我们平时使用promise是这样的:

const p1 = new Promise((resolve, reject) => {
    console.log('create a promise');
    setTimeout(()=>{
        console.log("异步")
        resolve('成功了');
    },10)
    
})
const p2 = p1.then((res)=>{
	console.log(res)
})

所以promise接收一个函数,该函数有两个参数,resolve和reject,并且同步执行这个函数。

// 先定义三个常量表示状态
var PENDING = 'pending';//等待
var FULFILLED = 'fulfilled';//执行完成
var REJECTED = 'rejected';//拒绝执行

function MyPromise(fn) {
    this.status = PENDING;    // 初始状态为pending
    this.value = null;        // 初始化value
    this.reason = null;       // 初始化reason
    //同步执行这个函数
    try {
        fn(resolve, reject);
    } catch (error) {
        reject(error);
    }
}
exports.MyPromise=MyPromise

可以看到fn(resolve, reject)异步操作执行完毕后,会调用resolve和reject,于是promise里面就需要创建resolve和reject提供调用。

// 这两个方法直接写在构造函数里面
function MyPromise(fn) {
  // ...省略前面代码...
  
  // 存一下this,以便resolve和reject里面访问
  var that = this;
  // resolve方法参数是value
  function resolve(value) {
    if(that.status === PENDING) {
      that.status = FULFILLED;
      that.value = value;
    }
  }
  
  // reject方法参数是reason
  function reject(reason) {
    if(that.status === PENDING) {
      that.status = REJECTED;
      that.reason = reason;
    }
  }
}

这样一来,就能在执行完promise包裹的异步操作后,调用resolve或者reject,从而改变promise的状态,并且取到结果value原因reason。

但是,就目前的代码而言,还需要then方法来获取结果并处理。

根据我们前面的分析then方法可以链式调用,所以他是实例方法,而且规范中的API是promise.then(onFulfilled, onRejected)我们先把架子搭出来:

MyPromise.prototype.then = function(onFulfilled, onRejected) {}

如此,我们在promise中会执行异步操作,异步操作完成后会调用resolve方法,修改promise的状态,并且把结果存在promise的value上。那现在如何在then方法中取得这个结果并执行呢?

首先需要判断该异步操作已经执行完毕(通过promise的状态变成FULFILLED)。异步操作的结果这时候已经存储到promise的value上了,于是只需要then的参数是函数,把这个value传进去即可

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    if(this.status === FULFILLED) {
        onFulfilled(this.value)//将结果传入回调函数
      }
    
    if(this.status === REJECTED) {
        onRejected(this.reason);
    }
}

我们现在来试试现在写的promise,写出如下代码:

var MyPromise = require('./test.js').MyPromise;
const p1 = new MyPromise((resolve, reject) => {
    console.log('create a promise');
    setTimeout(()=>{
        console.log("异步")
        resolve('成功了');
    },10)
    
  })
console.log("直接打印",p1)
setTimeout(()=>{
    console.log("过两s后",p1)
    const aaa=p1.then((value)=>{
        console.log(value)
    })
},2000)

打印的结果:

请添加图片描述

注意到,这里我们是使用setTimeout包裹了then函数,这意味着现在只是将结果取到了then中进行处理,还不能等待异步完成后再执行then中的代码。

到这里,就可以大致知道promise到底是个啥?

可以把它当作一个异步操作的处理站点

初始时状态是pending,当执行完毕异步操作(用户定义)后调用resolve/reject,修改状态变成fulfilled/rejected,并且把结果存储value/reason。

然后then方法判断异步操作是否完成,完成的话,就取value/reason来完成用户接下来的操作(用户定义)。

其实还是利用的回调函数,把promsie当作中转站,将异步操作和结果隔离开来。(异步操作放在promise的入参数中,结果放value/reason,then方法需要用到了再去这里取)。

请添加图片描述

2.4,then方法的回调函数收集与执行

实际上,我们使用promise的时候,不会像上文那样,用一个setTimeout来包裹,更多时候如下使用

new Promise(fn).then(onFulfilled, onRejected);

上面代码then是在实例对象创建好就调用了,这时候fn里面的异步操作可能还没结束,也就是说他的status还是PENDING,这时候肯定不能执行then里面的代码,因为结果还没有存储value上。

那怎么在异步操作完成后再执行then里面的代码呢?之前说过,resolve和reject方法就是在异步操作完成后调用的,所以可以在这里执行then传入的回调函数。

于是修改我们的promise:

// 构造函数
function MyPromise(fn) {
  // ...省略其他代码...
  
  // 构造函数里面添加个数存储成功和失败的回调
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];
  
  function resolve(value) {
    if(that.status === PENDING) {
      // ...省略其他代码...
      // resolve里面将所有成功的回调拿出来执行
      that.onFulfilledCallbacks.forEach(callback => {
        callback(that.value);
      });
    }
  }
  
  function reject(reason) {
    if(that.status === PENDING) {
      // ...省略其他代码...
      // resolve里面将所有失败的回调拿出来执行
      that.onRejectedCallbacks.forEach(callback => {
        callback(that.reason);
      });
    }
  }
}

// then方法
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // ...省略其他代码...
  // 如果还是PENDING状态,将回调保存下来
  if(this.status === PENDING) {
    this.onFulfilledCallbacks.push(onFulfilled);
    this.onRejectedCallbacks.push(onRejected);
  }
}

也就是说,promise.then做的事情,就是把传入的回调函数收集起来,然后在resolve或reject中取出收集好的回调函数来执行。

这样一来。就能保证then中的回调函数在异步操作结束后再执行。到这里我们已经初步实现了promise最核心的异步操作过程与结果的分离。

请添加图片描述

2.5,then的入参返回
2.5.1,then的入参

之前说过,then方法的入参不是函数则忽略其实所谓“忽略”并不是什么都不干,对于onFulfilled来说“忽略”就是将value原封不动的返回,对于onRejected来说就是返回reasononRejected因为是错误分支,我们返回reason应该throw一个Error,所以这里需要重写这两个入参:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    // 如果onFulfilled不是函数,给一个默认函数,返回value
    var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

    // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}
	//...省略其他代码
}
2.5.2,then的返回得是promise

then返回值必须是一个promise(这里使用promise2)。且如果onFulfilled或者onRejected抛出一个异常e,则这个新的promise2必须拒绝执行,并且返回原因e。也就是把onFulfilled和onRejected用trycatch包裹一层。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
   // 如果onFulfilled不是函数,给一个默认函数,返回value
   var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

  // // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

  var that=this
  var promise2 = new MyPromise(function(resolve, reject) {
        //如果异步已经成功则直接执行回调函数,期间有报错,需要reject
    if(that.status === FULFILLED) {
        try {
            realOnFulfilled(that.value);
        } catch (error) {
            reject(error);
        } 
    }
      //如果异步已经失败则直接执行回调函数,期间有报错,需要reject
    if(that.status === REJECTED) { 
        try {
            realOnRejected(that.reason);
        } catch (error) {
            reject(error);
        }
    }
      // 如果还是PENDING状态,将回调保存下来,为了捕获回调中的错误,需要try...catch包裹一层
    if(that.status === PENDING) {
        that.onFulfilledCallbacks.push(function() {
            try {
              realOnFulfilled(that.value);
            } catch (error) {
              reject(error);
            }
        });
        that.onRejectedCallbacks.push(function() {
            try {
              realOnRejected(that.reason);
            } catch (error) {
              reject(error);
            }
        });
      }
    })
  return promise2
}
2.5.3,then的入参默认是函数

而如果onFulfilled不是函数且promise1成功执行,那么promise2必须成功执行并且返回相同的值。这时候,就需要直接透传resolve(this.value)给promise2了。

如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的原因。

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    // 如果onFulfilled不是函数,给一个默认函数,返回value
    var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

    // // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

    var that=this
    //如果异步已经成功则直接执行回调函数,期间有报错,需要reject
    if(this.status === FULFILLED) {
        var promise2 = new MyPromise(function(resolve, reject) {
            try {
                //then方法传入非函数,且成功执行,则把结果传给promise2
                if (typeof onFulfilled !== 'function') {
                    resolve(that.value);
                } else {
                    realOnFulfilled(that.value);
                    resolve(that.value);
                }
            } catch (error) {
                reject(error);
            } 
        });
        return promise2;
    }
    //如果异步已经失败则直接执行回调函数,期间有报错,需要reject
    if(this.status === REJECTED) {        
        var promise2 = new MyPromise(function(resolve, reject) {
            try {
                //then方法传入非函数,且成功执行,则把结果传给promise2
                if (typeof onRejected !== 'function') {
                    reject(that.reason);
                } else {
                    realOnRejected(that.reason);
                    //promise1的onRejected执行成功后,promise2应该被resolve,这样promise2的状态才会是fulfilled
                    resolve();
                }
            } catch (error) {
                reject(error);
            }
        });
        return promise2;
    }
    // 如果还是PENDING状态,将回调保存下来,为了捕获回调中的错误,需要try...catch包裹一层
    if(this.status === PENDING) {
        var promise2 = new MyPromise(function(resolve, reject) {
            that.onFulfilledCallbacks.push(function() {
              try {
                  //如果传入的不是函数,则透传结果给promise2
                    if (typeof onFulfilled !== 'function') {
                        resolve(that.value);
                    } else {
                        realOnFulfilled(that.value);
                        resolve(that.value);
                    }
              } catch (error) {
                reject(error);
              }
            });
            that.onRejectedCallbacks.push(function() {
              try {
                  //如果传入的不是函数,则透传结果给promise2
                if (typeof onRejected !== 'function') {
                    reject(that.reason);
                } else {
                    realOnRejected(that.reason);
                    resolve()
                }
              } catch (error) {
                reject(error);
              }
            });
        });
        return promise2;
    }

}
2.5.4,如果传入then的回调函数有返回值

如果 onFulfilled 或者 onRejected 返回一个值 x ,需要区分x类别运行下面的 Promise 解决过程

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // 如果onFulfilled不是函数,给一个默认函数,返回value
  var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

  // // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

  var that=this
  var promise2 = new MyPromise(function(resolve, reject) {
        //如果异步已经成功则直接执行回调函数,期间有报错,需要reject
    if(that.status === FULFILLED) {
        try {
          if (typeof onFulfilled !== 'function') {
            resolve(that.value);
          } else {
            var x = realOnFulfilled(that.value);
            resolvePromise(promise2, x, resolve, reject);
          }
        } catch (error) {
            reject(error);
        } 
    }
      //如果异步已经失败则直接执行回调函数,期间有报错,需要reject
    if(that.status === REJECTED) { 
        try {
          if (typeof onRejected !== 'function') {
            reject(that.reason);
          } else {
            var x = realOnRejected(that.reason);
            resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
          }
        } catch (error) {
            reject(error);
        }
    }
      // 如果还是PENDING状态,将回调保存下来,为了捕获回调中的错误,需要try...catch包裹一层
    if(that.status === PENDING) {
        that.onFulfilledCallbacks.push(function() {
            try {
              if (typeof onFulfilled !== 'function') {
                resolve(that.value);
              } else {
                var x = realOnFulfilled(that.value);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
        });
        that.onRejectedCallbacks.push(function() {
            try {
              if (typeof onRejected !== 'function') {
                reject(that.reason);
              } else {
                var x = realOnRejected(that.reason);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
        });
      }
    })
  return promise2
}

在规范中还有一条onFulfilledonRejected 只有在执行环境堆栈包含平台代码时才可被调用。这一条的意思是实践中要确保 onFulfilledonRejected 方法异步执行,且应该then 方法被调用的那一轮事件循环之后的新执行栈中执行。所以在我们执行onFulfilledonRejected的时候都应该包到setTimeout里面去。

按照我个人的理解,这里包裹个setTimeout是为了让resolvePromise(promise2, x, resolve, reject);这里传入的promise2不是undefined

于是then方法变成:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // 如果onFulfilled不是函数,给一个默认函数,返回value
  var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

  // // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

  var that=this
  var promise2 = new MyPromise(function(resolve, reject) {
        //如果异步已经成功则直接执行回调函数,期间有报错,需要reject
    if(that.status === FULFILLED) {
      setTimeout(function() {
        try {
          if (typeof onFulfilled !== 'function') {
            resolve(that.value);
          } else {
            var x = realOnFulfilled(that.value);
            resolvePromise(promise2, x, resolve, reject);
          }
        } catch (error) {
            reject(error);
        } 
      },0)
    }
      //如果异步已经失败则直接执行回调函数,期间有报错,需要reject
    if(that.status === REJECTED) {  
      setTimeout(function() {
        try {
          if (typeof onRejected !== 'function') {
            reject(that.reason);
          } else {
            var x = realOnRejected(that.reason);
            resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
          }
        } catch (error) {
            reject(error);
        }
      },0)
    }
      // 如果还是PENDING状态,将回调保存下来,为了捕获回调中的错误,需要try...catch包裹一层
    if(that.status === PENDING) {
        that.onFulfilledCallbacks.push(function() {
          setTimeout(function () {
            try {
              if (typeof onFulfilled !== 'function') {
                resolve(that.value);
              } else {
                var x = realOnFulfilled(that.value);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
          },0)
        });
        that.onRejectedCallbacks.push(function() {
          setTimeout(function () {
            try {
              if (typeof onRejected !== 'function') {
                reject(that.reason);
              } else {
                var x = realOnRejected(that.reason);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
          },0)
        });
      }
    })
  return promise2
}
2.6,Promise 解决过程

从上文看,Promise 解决过程是为了处理then传入的函数带返回值的情况。于是针对不同返回值,需要有不同的处理方案。实现的原则就是一点:让then方法返回的promise2的值接收then入参函数的返回值

所以resolvePromise这样写:

resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程

把promise2传入,把then入参函数的返回x(可能没有)传入,然后传入promise2的resolve和reject,利用它来修改promise2的状态和值。

2.6.1,如果promise和x引用同一个对象,则用TypeError作为原因拒绝(reject)promise。
function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向一对象,以 TypeError 为据因拒绝执行 promise
    // 这是为了防止死循环
    if (promise === x) {
      return reject(new TypeError('The promise and the return value are the same'));
    }
}
2.6.2,如果then入参函数返回的是promise

如果x是一个promise,则需要递归逐层展开返回的promise取得结果y,并且把promise2和它的resolve和reject传入,只有这样,promise2才能得到最后的结果:

function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向一对象,以 TypeError 为据因拒绝执行 promise
    // 这是为了防止死循环
    if (promise === x) {
      return reject(new TypeError('The promise and the return value are the same'));
    }
  
    if (x instanceof MyPromise) {
      // 如果 x 为 Promise ,则让then返回的 promise 接受 x 的状态和结果
      // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
      x.then(function (y) {
        //注意这里传入的就是入参promise,也就是修改这个promise的状态和结果,简单写就是 resolve(y)
        resolvePromise(promise, y, resolve, reject);
      }, reject);
    }
}
2.6.3,如果x是个对象或者方法,这里处理的是类promise对象,即具备then方法的对象。
2.3.3另外,如果x是个对象或者方法,这里处理的是类promise对象,即具备then方法的对象。
    2.3.3.1 让x作为x.then
    2.3.3.2 如果取回的x.then属性的结果为一个异常e,用e作为原因reject promise
    2.3.3.3 如果then是一个方法,把x当作this来调用它, 第一个参数为 resolvePromise,第二个参数为rejectPromise,其中:
        2.3.3.3.1 如果/当 resolvePromise被一个值y调用,运行 [[Resolve]](promise, y)
        2.3.3.3.2 如果/当 rejectPromise被一个原因r调用,用r拒绝(reject)promise
        2.3.3.3.3 如果resolvePromise和 rejectPromise都被调用,或者对同一个参数进行多次调用,第一次调用执行,任何进一步的调用都被忽略
        2.3.3.3.4 如果调用then抛出一个异常e,
            2.3.3.3.4.1 如果resolvePromise或 rejectPromise已被调用,忽略。
            2.3.3.3.4.2 或者, 用e作为reason拒绝(reject)promise
	2.3.3.4 如果then不是一个函数,用x完成(fulfill)promise

这里规范中讲得很复杂其实就是为了处理返回的是类promsie(thenable)对象。

如下代码:

var thenableA = {
    name: 'thenableA',
    then: function (resolve, reject) {
        console.log(`I'am ${this.name}`);
        resolve(this.name)
    }
}
var p1=new MyPromise((resolve, reject) => {
    console.log('create promise1');
    resolve(1)
})
p1.then((res) => {
    return thenableA
})

打印出来就是:

create promise1
I'am thenableA

具体的then方法实现的内容,就是把thenable对象像promise一样展开,执行其中的then方法,同样是让then方法返回的promise2的值接收最终的值。

function resolvePromise(promise, x, resolve, reject) {
    //...其他代码
    // 如果 x 为对象或者函数,即可能是thenable的对象/函数
    else if (x  &&(typeof x === 'object' || typeof x === 'function')) {
      var called = false;
      try {
        // 把 x.then 赋值给 then 
        var then = x.then;
        // 如果 then 是函数,则返回的其实是类promise(thenable)对象,需要展开执行其中的then方法
        if (typeof then === 'function') {
            then.call(
              x,
              function (y) {
                if (called) return;
                called = true;
                resolvePromise(promise, y, resolve, reject);//把它的then方法执行掉,并且修改promise2的值和状态
              },
              function (r) {
                if (called) return;
                called = true;
                reject(r);
              });
        } else {
          // 如果 then 不是函数(then返回的不是thenable对象),以 x 为参数执行 promise
          if (called) return;
          called = true;
          resolve(x);
        }
      } catch (error) {
        // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise,return是为了不执行后续的代码
        if (called) return;
        called = true;
        return reject(error);
      }
}

例如上文代码:

var thenableA = {
    name: 'thenableA',
    then: function (resolve, reject) {
        console.log(`I'am ${this.name}`);
        resolve(this.name)
    }
}
var p1=new MyPromise((resolve, reject) => {
    console.log('create promise1');
    resolve(1)
})
var p2=p1.then((res) => {
    return thenableA
})

setTimeout(()=>{
    console.log("+++++",p2)
},0)

执行之后打印的是:

create promise1
I'am thenableA
+++++ MyPromise {
  status: 'fulfilled',
  value: 'thenableA',
  reason: null,
  onFulfilledCallbacks: [],
  onRejectedCallbacks: []
}

可以看到thenableA的then方法被执行,并且改变了promise2的值,于是p2的value变成了’thenableA’。

2.6.4,返回的x不是函数也不是对象,甚至没有返回(x为undefined

如果 x既不是对象也不是函数,用x完成(fulfill)promise,也就是直接resolve(x)即可

三,验证promiseA+规范

在我们写好的MyPromsie添加如下代码:

MyPromise.deferred = function() {
  var result = {};
  result.promise = new MyPromise(function(resolve, reject){
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}
module.exports = MyPromise//记得这一步必须这样导出,不然会报错。

然后项目目录安装测试库:

npm i promises-aplus-tests -g

于是我们的手手写promise完整内容如下:


// 先定义三个常量表示状态
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';

function MyPromise(fn) {
    this.status = PENDING;    // 初始状态为pending
    this.value = null;        // 初始化value
    this.reason = null;       // 初始化reason
    // 构造函数里面添加个数存储成功和失败的回调
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    // 存一下this,以便resolve和reject里面访问
    var that = this;
    // resolve方法参数是value
    function resolve(value) {
        if(that.status === PENDING) {
            that.status = FULFILLED;
            that.value = value;
            // resolve里面将所有成功的回调拿出来执行
            that.onFulfilledCallbacks.forEach(callback => {
                callback(that.value);
            });
        }
    }

    // reject方法参数是reason
    function reject(reason) {
        if(that.status === PENDING) {
            that.status = REJECTED;
            that.reason = reason;
            // resolve里面将所有失败的回调拿出来执行
            that.onRejectedCallbacks.forEach(callback => {
                callback(that.reason);
            });
        }
    }

    try {
        fn(resolve, reject);
    } catch (error) {
        reject(error);
    }
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // 如果onFulfilled不是函数,给一个默认函数,返回value
  var realOnFulfilled=typeof onFulfilled === 'function' ? onFulfilled : value=>value

  // // 如果onRejected不是函数,给一个默认函数,返回reason的Error
	var realOnRejected =typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

  var that=this
  var promise2 = new MyPromise(function(resolve, reject) {
        //如果异步已经成功则直接执行回调函数,期间有报错,需要reject
    if(that.status === FULFILLED) {
      setTimeout(function() {
        try {
          if (typeof onFulfilled !== 'function') {
            resolve(that.value);
          } else {
            var x = realOnFulfilled(that.value);
            resolvePromise(promise2, x, resolve, reject);
          }
        } catch (error) {
            reject(error);
        } 
      },0)
    }
      //如果异步已经失败则直接执行回调函数,期间有报错,需要reject
    if(that.status === REJECTED) {  
      setTimeout(function() {
        try {
          if (typeof onRejected !== 'function') {
            reject(that.reason);
          } else {
            var x = realOnRejected(that.reason);
            resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
          }
        } catch (error) {
            reject(error);
        }
      },0)
    }
      // 如果还是PENDING状态,将回调保存下来,为了捕获回调中的错误,需要try...catch包裹一层
    if(that.status === PENDING) {
        that.onFulfilledCallbacks.push(function() {
          setTimeout(function () {
            try {
              if (typeof onFulfilled !== 'function') {
                resolve(that.value);
              } else {
                var x = realOnFulfilled(that.value);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
          },0)
        });
        that.onRejectedCallbacks.push(function() {
          setTimeout(function () {
            try {
              if (typeof onRejected !== 'function') {
                reject(that.reason);
              } else {
                var x = realOnRejected(that.reason);
                resolvePromise(promise2, x, resolve, reject);   // 调用Promise 解决过程
              }
            } catch (error) {
              reject(error);
            }
          },0)
        });
      }
    })
  return promise2
}

function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向一对象,以 TypeError 为据因拒绝执行 promise
    // 这是为了防止死循环
    if (promise === x) {
      return reject(new TypeError('The promise and the return value are the same'));
    }
  
    if (x instanceof MyPromise) {
      console.log("是MyPromise")
      // 如果 x 为 Promise ,则让then返回的 promise 接受 x 的状态和结果
      // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
      x.then(function (y) {
        //注意这里传入的就是入参promise,也就是修改这个promise的状态和结果,简单写就是 resolve(y)
        resolvePromise(promise, y, resolve, reject);
      }, reject);
    }
    // 如果 x 为对象或者函数
    else if (x  &&(typeof x === 'object' || typeof x === 'function')) {
      var called = false;
      try {
        // 把 x.then 赋值给 then 
        var then = x.then;
        // 如果 then 是函数,则返回的其实是类promise(thenable)对象,需要展开执行其中的then方法
        if (typeof then === 'function') {
            then.call(
              x,
              function (y) {
                if (called) return;
                called = true;
                resolvePromise(promise, y, resolve, reject);
              },
              function (r) {
                if (called) return;
                called = true;
                reject(r);
              });
        } else {
          // 如果 then 不是函数(then返回的不是thenable对象),以 x 为参数执行 promise
          if (called) return;
          called = true;
          resolve(x);
        }
      } catch (error) {
        // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise,return是为了不执行后续的代码
        if (called) return;
        called = true;
        return reject(error);
      }
    } else {
      // 如果 x 不为对象或者函数,以 x 为参数执行 promise,没有返回时也执行这个
      resolve(x);
    }
}

MyPromise.deferred = function() {
  var result = {};
  result.promise = new MyPromise(function(resolve, reject){
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}
module.exports = MyPromise

项目根目录执行测试

promises-aplus-tests test.js(test.js是我的文件名

可以看到通过全量测试用例

请添加图片描述

四,promise.resolve的实现

它的作用是将现有对象转为Promise对象。

即,如果是promise则直接返回,如果不是则返回值为这个的promise。

MyPromise.resolve = function(parameter) {
  if(parameter instanceof MyPromise) {
    return parameter;
  }

  return new MyPromise(function(resolve) {
    resolve(parameter);
  });
}

即:

var promise1 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(2)
    },10)
})
var p2=MyPromise.resolve(promise1)
p2.then((res)=>{
    console.log(res)//打印2
})

五,Promise.reject的实现

返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递实例的回调函数。

MyPromise.reject = function(reason) {
  return new MyPromise(function(resolve, reject) {
    reject(reason);
  });
}

六,Promise.all的实现

该方法用于多个 Promise 实例,包装成一个新的 Promise 实例。它接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。当p1, p2, p3全部resolve,外层的promise才resolve,有任何一个reject,外层的promise都reject。

const p = Promise.all([p1, p2, p3]);
MyPromise.all = function(promiseList) {
  var resPromise = new MyPromise(function(resolve, reject) {
    var count = 0;
    var result = [];
    var length = promiseList.length;

    if(length === 0) {
      return resolve(result);
    }

    promiseList.forEach(function(promise, index) {
      MyPromise.resolve(promise).then(function(value){
        count++;
        result[index] = value;
        if(count === length) {
          //全部执行完毕后才resolve结果数组出去
          resolve(result);
        }
      }, function(reason){
        reject(reason);
      });
    });
  });

  return resPromise;
}

使用示例

var p1 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(1)
    },10)
})
var p2 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(2)
    },0)
})
var p3 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(3)
    },20)
})

var result=MyPromise.all([p1,p2,p3])
setTimeout(()=>{
    console.log(result)//打印返回的所有结果
},1000)
result.then((res)=>{
    console.log(res)
})

得到的结果:

[外链图片转存失败,源站可能防盗链机制,建议图片保存下来直接上传(img-Z0tyj5fd-1680622612470)(C:UsersAdministratorAppDataRoamingTyporatyporauserimagesimage-20230329225315698.png)]

七,Promise.race方法

和promise.all的用法一样,传入一个promsie数组不同的是,promise.race是一旦有一个promise执行完毕后,就返回结果。

MyPromise.race = function(promiseList) {
  var resPromise = new MyPromise(function(resolve, reject) {
    var length = promiseList.length;

    if(length === 0) {
      return resolve();
    } else {
      for(var i = 0; i < length; i++) {
        //哪个先成功就直接resolve结果了,因为return了,就不执行后续的结果了,不用foreach是因为它无法return
        MyPromise.resolve(promiseList[i]).then(function(value) {
          return resolve(value);
        }, function(reason) {
          return reject(reason);
        });
      }
    }
  });
  return resPromise;
}

使用示例

var p1 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(1)
    },10)
})
var p2 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(2)
    },0)
})
var p3 = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(3)
    },20)
})

var result=MyPromise.race([p1,p2,p3])
setTimeout(()=>{
    console.log(result)//打印返回的所有结果
},1000)
result.then((res)=>{
    console.log(res)
})

八,Promise.prototype.catch

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)别名用于指定发生错误时的回调函数。

MyPromise.prototype.catch = function(onRejected) {
  this.then(null, onRejected);
}

九,总结

9.1,promise做到异步操作和结果的分离

这一点是promise存在的最大价值体现。它的实现,其实就是让then方法传入的函数在异步操作后执行即可。这里为了方便描述,我只说成功的情况。

一种情况是在调用then方法的时候,异步操作还未完成(promise的状态是pedding)。大致的流程如下:

【这里本来是动图的,但是csdn不允许上传这么大的动图,,,具体的可以看我公众号吧,嘻嘻,我最近开始写公众号啦!撒花!】

另一种情况是,在调用then方法的时候,异步操作已经完成(promise的状态已经变成fulfilled)。这时候收集器就不用收集回调函数了,而是可以直接执行,大致的流程如下:

【这里本来是动图的,但是csdn不允许上传这么大的动图,,,具体的可以看我公众号吧,嘻嘻,我最近开始写公众号啦!撒花!】

9.2,then能链式调用是因为then返回是个新的promise

我们为了解决回调地狱使用promise时,会进行链式调用。这是因为then方法返回一个新的promise。

并且这个新的promise倾向于返回最后的处理结果

这句话怎么理解呢?还是只说成功情况的处理。

1,then不传入函数参数时,透传promise的处理结果。
2,then传入的函数参数没有返回时,也就是返回值undefined,那么返回的新promise的value值是undefined。
3,then传入的函数参数有返回值,且返回值是promise,则取得这个promse最后的结果,传递给它返回的promise。
4,then传入的函数参数有返回值,且返回值是thenable对象,和promise差不多,就是执行thenable对象的then方法,取得这个promise最后的结果,传递给它返回的promise。
5,then传入的函数参数有返回值,且返回值是正常数据,则传递给它返回的promise。

原文地址:https://blog.csdn.net/weixin_42349568/article/details/129964391

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_37282.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注