【Promise】自定义Promise手写 (四)

目录

Ⅰ-Promise的实例方法实现

1 - 初始结构搭建

2 - resolve 与 reject构建与基础实现

3 - throw 抛出异常改变状态

4 - 状态只能修改一次

5 - then 方法执行回调基础实现

6 - 异步任务 then 方法实现

7 - 指定多个回调

8 - 同步任务 then 返回结果

9 - 异步任务 then 返回结果

10- then方法代码优化

11 - catch 方法与异常穿透与值传递


Ⅰ-Promise的实例方法实现

1 - 初始结构搭建

html引入,该章节后续html大部分重复 除非必要,否则不再放上来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-封装 | 1 - 初始结构搭建</title>
    <script src="./promise.js"></script>
</head>
<body>
    <script>
        let p = new Promise((resolve, reject) => {
            resolve('OK');
        });
        p.then(value => {
            console.log(value);
        }, reason=>{
            console.warn(reason);
        })
    </script>
</body>
</html>

promise.js -->使用原生写法,最后会改为class写法

function Promise(executor){}
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){}

2 - resolve 与 reject构建与基础实现

  1. 使用const self = this;保存this执行,使function中可以取得当前实例

ps:可以不使用该方法保存,但是下方function需要改为箭头函数,否则function默认指向是window

之后代码默认使用self保存this,箭头函数方式将在最后改为class写法时使用

  1. 默认设置 PromiseState = 'pending'以及 PromiseResult = null,这就是promise状态基础
//声明构造函数
function Promise(executor) {
  //添加属性
  this.PromiseState = 'pending';
  this.PromiseResult = null;
  //保存实例对象的 this 的值
/*  此处可以不写,但是下面function方法需要改为箭头函数,否则function默认指向是window */
  const self = this; 
  //resolve 函数
  function resolve(data) {--------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'fulfilled'; // resolved
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //reject 函数
  function reject(data) {----------------------------------------------
    //1. 修改对象的状态 (promiseState)
    self.PromiseState = 'rejected'; // 
    //2. 设置对象结果值 (promiseResult)
    self.PromiseResult = data;
  }
  //同步调用『执行器函数』
  executor(resolve, reject);
}
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {}

3 - throw 抛出异常改变状态

  1. 在2的基础上进行修改:将执行器放入try-catch()
  2. 在catch中使用reject()修改 promise 对象状态为『失败
 try {
    //同步调用『执行器函数』
    executor(resolve, reject);
  } catch (e) {
    //修改 promise 对象状态为『失败』
    reject(e);
  }

4 - 状态只能修改一次

  1. 基于2 3代码中resolve和reject方法进修改

  2. 在成功与失败函数中添加判断 if(self.PromiseState !== 'pending') return;,如果进入函数时状态不为pending直接退出,这样就能做到状态只能从pending改至其他状态且做到只能改一次

html调用--------------------------------------------------------
 let p = new Promise((resolve, reject) => {
      reject("error");
      resolve('OK');
      //抛出异常
      // throw "error";
    });
 console.log(p);
promise.js修改--------------------------------------------------------

  //resolve 函数
    function resolve(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'fulfilled';// resolved
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }
    //reject 函数
    function reject(data){
        //判断状态
        if(self.PromiseState !== 'pending') return;
        //1. 修改对象的状态 (promiseState)
        self.PromiseState = 'rejected';// 
        //2. 设置对象结果值 (promiseResult)
        self.PromiseResult = data;
    }

5 - then 方法执行回调基础实现

  1. 修改Promise.prototype.then方法
  2. 传入then(成功回调,失败回调),当调用then后,会判断当前this.PromiseState的状态,当其为成功时调用成功回调,失败时调用失败回调
html调用------------------------------------------------------------
    let p = new Promise((resolve, reject) => {
      // resolve('OK');// reject("Error");
      throw "ERROR";
    });
    p.then(
        value => {console.log(value); }, 
        reason => {console.warn(reason);}
    )
promise.js修改与实现-----------------------------------------------------
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  //调用回调函数  PromiseState
  if (this.PromiseState === 'fulfilled') {onResolved(this.PromiseResult);}
  if (this.PromiseState === 'rejected') {onRejected(this.PromiseResult);}
}

6 - 异步任务 then 方法实现

  1. 此处对于5有四处修改,下面上js代码

  2. 当我运行异步代码后,我的执行器内部代码还未返回(因为用了定时器,里面的代码进入了异步队列),所以当我下面的.then()运行时:我的ppending状态,所以根本不会执行resolve与reject方法

解:添加判断pending状态,将当前回调函数保存到实例对象(存到实例上是为了更方便)中,这样后续改变状态时候才调用得到

  1. 为什么要将回调保存到实例上而不是直接调用?

理由:因为我的回调函数需要在我的promise状态改变后(成功或者失败),再根据状态选择运行哪个函数 所以当你调用then()时却检测到状态为pending,说明这时候的promise在异步队列 不能直接运行成功或者失败函数

解决:因为resolve与reject方法与then()不在同一个作用域中,并不能共享then(成功回调,失败回调)的参数,所以在判断状态为pending时将回调保存到实例对象上.然后将回调函数的调用放在resolve()与reject()

这样当我代码运行到异步队列的resolve()或reject()时,就可以在这个函数中运行回调函数,实现异步then

  1. 此处的then仍有瑕疵,需要继续完善
html调用------------------------------------------------------------
 //实例化对象
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {reject("error"); /* resolve('OK');*/}, 1000);
    });
    p.then(value => {console.log(value);},reason => { console.warn(reason);});
    console.log(p);

promise.js修改与实现-----------------------------------------------------
//声明构造函数
function Promise(executor) {
  this.PromiseState = 'pending'; this.PromiseResult = null;
  // 声明属性     
  this.callback = {};			-----------新添加1
  const self = this; 
    
  //resolve 函数
  function resolve(data) {
    //判断状态
    if (self.PromiseState !== 'pending') return;
    self.PromiseState = 'fulfilled'; self.PromiseResult = data;
    //调用成功的回调函数  加判断的原因是防止无回调报错
    if (self.callback.onResolved) { self.callback.onResolved(data); }  ------------新添加2 最重要 
  }
    
  //reject 函数
  function reject(data) {
    if (self.PromiseState !== 'pending') return;
    self.PromiseState = 'rejected'; self.PromiseResult = data;
    //执行回调						
    if (self.callback.onResolved) { self.callback.onResolved(data);}  ------------新添加3
  }
  try {executor(resolve, reject);} catch (e) {reject(e);}
}

//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  //调用回调函数  PromiseState
  if (this.PromiseState === 'fulfilled') {onResolved(this.PromiseResult);}
  if (this.PromiseState === 'rejected') { onRejected(this.PromiseResult);}
  //判断 pending 状态
  if (this.PromiseState === 'pending') {  ------------新添加4
    //保存回调函数
    this.callback = {
      onResolved: onResolved,
      onRejected: onRejected
    }
  }
}

7 - 指定多个回调

  1. 基于6代码进行修改 只展示修改部分代码

  2. 6中保存回调函数的方式有BUG,如果我有多个.then(),后面加载的回调函数会覆盖之前的回调函数,导致最后回调函数有且只有最后一个

解:使用数组的方式进行存储回调函数,调用时也是用数组循环取出

  1. 此处的then仍有瑕疵,需要继续完善
html调用------------------------------------------------------------
//实例化对象
   let p = new Promise((resolve, reject) => {setTimeout(() => {reject('No');}, 1000);});
   p.then(value => { console.log(value);}, reason=>{console.warn(reason);});
   p.then(value => { alert(value);}, reason=>{ alert(reason);});
   console.log(p);

promise.js修改与实现-----------------------------------------------------
Promise.prototype.then = function (onResolved, onRejected) {
       //resolve 函数
    function resolve(data){
  		.....
        //调用成功的回调函数
        // if (self.callback.onResolved) { self.callback.onResolved(data); } 
        self.callbacks.forEach(item => {   --------修改1
            item.onResolved(data);
        });
    }
    //reject 函数
    function reject(data){
     	 ......
        //执行失败的回调
        // if (self.callback.onResolved) { self.callback.onResolved(data);}
        self.callbacks.forEach(item => {		------修改2
            item.onRejected(data);
        });
    }
    
  //添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    ........
    //判断 pending 状态
    if(this.PromiseState === 'pending'){
        //保存回调函数
        //  this.callback = { onResolved: onResolved, onRejected: onRejected }
        this.callbacks.push({					--------修改3
            onResolved: onResolved,
            onRejected: onRejected
        });
    }
}

8 - 同步任务 then 返回结果

  1. 在之前的then运行结果中得知,我们使用 [ then ] 后的返回结果是其回调函数的返回结果,而我们需要的返回结果是一个新的promise对象

解:所以我们在then中return new Promise(),使其得到的是一个新的promise对象

  1. 在为解决问题1后产生一个新问题:新的promise对象因为没有用rejerect与resolve方法,导致返回的状态一直是pending

解:在新的promise中判断运行回调函数后的返回值是什么,然后根据其不同类型给其赋予不同状态

​ Ⅰ-if(result instanceof Promise):返回值一个新的②promise对象(因为是新的promise的回调函数返回值,称②promise对象),在返回值(因为是promise对象)的.then()回调函数中使用rejerect与resolve方法,将其自身的状态赋予外层的promise,

​ 即 回调函数中的promise 赋值 给then返回值 , 所以 最终返回状态==回调函数中的新promise状态

​ Ⅱ-如果返回值是一个非promise对象,返回状态设置为成功

​ Ⅲ-如果返回值是一个异常,返回状态设置为失败

html调用------------------------------------------------------------
  //实例化对象
    let p = new Promise((resolve, reject) => {resolve('OK');});
    //执行 then 方法
    const res = p.then(
     value => { throw "FAIL";},
    reason => { console.warn(reason);});
    console.log(res);

promise.js修改与实现-----------------------------------------------------
//添加 then 方法
Promise.prototype.then = function(onResolved, onRejected){
    return new Promise((resolve, reject) => {
        //调用回调函数  PromiseState
 //  if(this.PromiseState === 'fulfilled'){ onResolved(this.PromiseResult);} 未修改时代码
        if(this.PromiseState === 'fulfilled'){    -------修改1 
            try{
                //获取回调函数的执行结果
                let result = onResolved(this.PromiseResult);
                //判断
                if(result instanceof Promise){//如果是 Promise 类型的对象,我就将下一个promise结果赋予外层
                    result.then(v => {  resolve(v); },r=>{reject(r);})
                }else{resolve(result);}  //如果返回的不是promise对象,都将其赋予成功状态
            }catch(e){
                rejerect(e);	//如果出错了,则返回失败状态
            }
        }
        if(this.PromiseState === 'rejected'){ onRejected(this.PromiseResult);}------此部分修改与修改1一样
        //判断 pending 状态
        if(this.PromiseState === 'pending'){
            this.callbacks.push({ onResolved: onResolved, onRejected: onRejected});
        }
    })
}

9 - 异步任务 then 返回结果

  1. 异步任务是修改if(this.PromiseState === 'pending')后面的值,原因参考6,下面代码只举例这部分修改

  2. 因为我们需要增加then状态修改,所以在我们保存回调函数这一步我们可以对于回调函数进行加工,添加判断其回调函数的返回值的代码块再存入实例的回调函数中

Ⅰ-声明一个新的函数:其内部功能->先运行onResolved回调函数,再将其返回值取出,进行判断其返回值(这个过程同8)

Ⅱ-加工后存入实例回调函数数组,之后在resolve与reject方法中调用即可(同6)

html调用------------------------------------------------------------
   //实例化对象
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {reject("Error");}, 1000)}); // resolve('OK');
    //执行 then 方法
    const res = p.then(value => {
      // return 'oh Yeah';  //如果有返回,根据其返回值得到相应的状态:字符串为成功,抛出为错误
      throw 'error';
    }, reason => {
      console.warn(reason, "xx"); //如果只是打印没返回,则实际上时返回一个undefined,
      //在我们封装js中,undefined会判定为非promise对象,所以状态为成功,结果为undefined
      return "sss"   // throw 'error';
    });
    console.log(res);

promise.js修改与实现-----------------------------------------------------
    //判断 pending 状态
    if (this.PromiseState === 'pending') {
      //保存回调函数
      this.callbacks.push({
          
        onResolved: function () {
          try {
            //执行成功回调函数
            let result = onResolved(self.PromiseResult);
            //判断 其结果
            if (result instanceof Promise) {
              result.then(
                  v => { resolve(v);},
                  r => {reject(r);}
                 )
            } else {resolve(result);}
          } catch (e) {reject(e);}
        },
          
        onRejected: function () {
          try {
            //执行成功回调函数
            let result = onRejected(self.PromiseResult);
            //判断
            if (result instanceof Promise) {
              result.then(
                  v => {resolve(v); },
                  r => {reject(r);}
                 )
            } else {resolve(result);}
          } catch (e) { reject(e); }
        }
      });
    }

10- then方法代码优化

  1. 在8、9、10中可以看出,其判断与改变返回结果状态的代码块是基本重复的,所以可以将其抽出
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  const self = this;
  return new Promise((resolve, reject) => {
    封装函数----------------------------------------------------------------------------
    function callback(type) {
      try {
        //获取回调函数的执行结果
        let result = type(self.PromiseResult);
        //判断
        if (result instanceof Promise) {
          //如果是 Promise 类型的对象
          result.then(v => {
            resolve(v);
          }, r => {
            reject(r);
          })
        } else {
          //结果的对象状态为『成功』
          resolve(result);
        }
      } catch (e) {
        reject(e);
      }
    }
  -----------------------------------------------------------------------------------    
    //调用回调函数  PromiseState
    if (this.PromiseState === 'fulfilled') {
      callback(onResolved);
    }
    if (this.PromiseState === 'rejected') {
      callback(onRejected);
    }
    //判断 pending 状态
    if (this.PromiseState === 'pending') {
      //保存回调函数
      this.callbacks.push({
        onResolved: function () {
          callback(onResolved);
        },
        onRejected: function () {
          callback(onRejected);
        }
      });
    }
  })
}

11 - catch 方法与异常穿透与值传递

  1. 异常穿透:添加catch 方法 ,并且需要进行回调函数为undefined的处理

  2. 当我then()中只传一个回调或者不传回调函数时,运行代码会报错,因为运行时调用的回调函数是undefined

解:进行回调函数判断,当其为空时,基于默认回调函数内容:直接往外抛出这样下方的then() or catch()就可以承接到异常或者值

html调用------------------------------------------------------------  
//实例化对象
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {resolve('OK'); }, 1000);
    });
    //值传递
    p.then()
    .then(value => {console.log(222);})
      .then(value => {console.log(333);})
        .catch(reason => {console.warn(reason);});
promise.js修改与实现-----------------------------------------------------
//添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
	...				-----------修改1
  if (typeof onRejected !== 'function') {onRejected = reason => { throw reason;}}
  if (typeof onResolved !== 'function') { onResolved = value => value;}
	 ....
}
//添加 catch 方法  
Promise.prototype.catch = function(onRejected){  ---------------异常穿透 修改2
    return this.then(undefined, onRejected);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值