promise的模拟实现

1.概述

1.1 什么是promise

Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口。它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。

1.2 promise解决的问题

1.2.1 回调地狱

Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。

1.2.2 并行结果

如果几个异步操作之间并没有前后顺序之分,但需要等多个异步操作都完成后才能执行后续的任务,无法实现并行节约时间。

1.3 promise简介

ES6 Promises 标准中定义的API还不是很多,目前大致有下面三种类型。

1.3.1 consturctor

首先,Promise 是一个对象,也是一个构造函数。

function f1(resolve, reject) {
  // 异步代码...
}

var promise = new Promise(f1);
复制代码

1.3.2 Instance Method

Promise对象通过自身的状态,来控制异步操作。Promise实例具有三种状态。

  • 异步操作未完成(pending)
  • 异步操作成功(fulfilled)
  • 异步操作失败(rejected)

这三种的状态的变化途径只有两种。

  • 从“未完成”到“成功”
  • 从“未完成”到“失败”

所以Promise的最终结果只有两个,

  • 异步操作成功,Promise实例传回一个值(value),状态变为fulfilled
  • 异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected
promise.then(onFulfilled, onRejected)
// resolve(成功)时,onFulfilled 会被调用
// reject(失败)时,onRejected 会被调用
复制代码

1.3.3 Static Method

Promise这样的全局对象还拥有一些静态方法。包括 Promise.all()还有Promise.resolve() 等在内,主要都是一些对Promise进行操作的辅助方法。

2.Promise的实现

2.1 resolve和reject

resolvereject都是函数

//构造函数中
function Promise(executor) {
    let self = this;
    
    /*初始化status*/
    self.status = 'pending';
    /*初始化value*/
    self.value = undefined;
    /*订阅事件的数组*/
    self.onResolvedCallBacks = [];
    self.onRejectedCallBacks = [];
    
    /*此函数将Promise实例的状态由pending 转化为 fulfilled*/
    function resolve(value) {
        if (value instanceof Promise) {
            return value.then(resolve, reject);
        }
        setTimeout(function () {
            if (self.status === 'pending') {
                self.status = 'fulfilled';
                self.value = value;
                /*发布已经订阅过的事件*/
                self.onResolvedCallBacks.forEach(item => item(self.value))
            }
        }, 0)
    }
    /*此函数将Promise实例的状态由pending 转化为 rejected*/
    function reject(reason) {
        setTimeout(function () {
            if (self.status === 'pending') {
                self.status = 'rejected';
                self.value = reason;
                /*发布已经订阅过的事件*/
                self.onRejectedCallBacks.forEach(item => item(self.value))
            }
        }, 0)
    }
    
    // new Promise 的时候,执行器(executor)的代码会立即执行
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
    
}
复制代码

2.2 then方法

promise成功后,onFullfilled会被调用。并且把promise的值当做它的第一个参数。promise在成功之前,不会调用它,并且只能被调用一次。reject也一样。调用 then 方法后,返回一个全新的 promise

// 先封装一个方法
function resolvePromise(promise2,x,resolve,reject){
//
  if(promise2 === x){
    return reject(new TypeError('循环引用'));
  }
  // 为了防止resolve和reject同时调用
  let called = false;//promise2是否已经resolve 或reject了
  if(x instanceof Promise){
    if(x.status == PENDING){
      x.then(function(y){
        resolvePromise(promise2,y,resolve,reject);
      },reject);
    }else{
      x.then(resolve,reject);
    }
    //x是一个thenable对象或函数,只要有then方法的对象,
  }else if(x!= null &&((typeof x=='object')||(typeof x == 'function'))){
    //当我们的promise和别的promise进行交互,编写这段代码的时候尽量的考虑兼容性,
    //允许别人瞎写,x可以是对象,也可以是函数
    try{
      let then = x.then;
      if(typeof then == 'function'){
        //有些promise会同时执行成功和失败的回调
        then.call(x,function(y){
          //如果promise2已经成功或失败了,则不会再处理了
          if(called)return;
          called = true;
          resolvePromise(promise2,y,resolve,reject)
        },function(err){
          if(called)return;
          called = true;
          reject(err);
        });
      }else{
        //到此的话x不是一个thenable对象,那直接把它当成值resolve promise2就可以了
        // 当返回一个对象 {name:'a',then:{age:8}},对象里的then不是thenable对象
        resolve(x);
      }
    }catch(e){
      if(called)return;
      called = true;
      reject(e);
    }

  }else{
    //如果X是一个普通 的值,则用x的值去resolve promise2
    resolve(x);
  }
}

Promise.prototype.then = function(onFulfilled,onRejected){
  //如果成功和失败的回调没有传,则表示这个then没有任何逻辑,只会把值往后抛,叫做值的穿透
  // 例如:promise.then().then().then(function(data){},function(err){})
  
  onFulfilled = typeof onFulfilled == 'function'?onFulfilled:function(value){return  value};
  onRejected = typeof onRejected == 'function'?onRejected:reason=>{throw reason};
  //如果当前promise状态已经是成功态了,onFulfilled直接取值
  let self = this;
  let promise2;
  if(self.status == FULFILLED){
    return promise2 = new Promise(function(resolve,reject){
      setTimeout(function(){
        try{
          let x =onFulfilled(self.value);
          //如果获取到了返回值x,会走解析promise的过程
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          //如果执行成功的回调过程中出错了,用错误原因把promise2 reject
          reject(e);
        }
      })

    });
  }
  if(self.status == REJECTED){
    return promise2 = new Promise(function(resolve,reject){
      setTimeout(function(){
        try{
          let x =onRejected(self.value);
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          reject(e);
        }
      })
    });
  }
  if(self.status == PENDING){
    return promise2 = new Promise(function(resolve,reject){
      self.onResolvedCallbacks.push(function(){
        try{
          let x =onFulfilled(self.value);
          //如果获取到了返回值x,会走解析promise的过程
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          reject(e);
        }

      });
      self.onRejectedCallbacks.push(function(){
        try{
          let x =onRejected(self.value);
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          reject(e);
        }
      });
    });
  }

}
复制代码

2.2 catch方法

Promise.prototype.catch=function (callback) {
  return this.then(null,callback);
}
---------使用-------
let promise = new Promise(function (resolve, reject) {
  reject('错误');
})
promise.then(function(data){console.log(data)}).catch(e=>{console.log(e)});

复制代码

2.3 all 方法

Promise.all = function (promises) {
  return new Promise(function (resolve,reject) {
    let arr =[];//arr是最终返回值的结果的集合。
    let j =0;
    function processData(i,data){// 每调用一次此函数,j就会+1;
      arr[i] = data;// 每次成功的结果都放入数组中
      if(++j===promises.length){
        resolve(arr); //只有promises中的最后一个成功,才能调用resolve方法。
      }
    }
    for(let i=0;i<promises.length;i++){
      promises[i].then(function (data) {
          processData(i,data);
      },reject)// 如果有一个失败,整体就会失败
    }
  })
}
复制代码

使用方法:

function read(file) {
  return new Promise1(function (resolve, reject) {
    require('fs').readFile(file,'utf8',function (err, data) {
      resolve(data);
    })
  })
}

Promise1.all([read('./1.txt'),read('./2.txt')]).then(function (data) {
  console.log(data)
})
// ['file1','file2']
复制代码

2.4 race 方法

只要有一个promise成功,就会执行成功的回调,参数是由promise组成的数组

Promise.race=function (promises) {
  return new Promise(function (resolve, reject) {
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject);
    }
  })
}
复制代码

2.5 resolve方法

Promise.resolve = function (value) {
  return new Promise(function (resolve, reject) {
    resolve(value)
  })
}
复制代码

2.6 reject 方法

Promise.reject = function (reason) {
  return new Promise(function (resolve, reject) {
    reject(reason);
  })
}
复制代码

3.Promise对象的缺点:

  • 1、无法取消Promise,一旦新建它就会立即执行,无法中途取消。

  • 2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

  • 3、当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值