promise代码实现代码和重点笔记

3 篇文章 0 订阅

promise

概念

一种异步编程的思路,一种以同步形式来书写异步代码的技术

状态

promise一共有3种状态

  • pending 进行中
  • fulfilled 已成功
  • rejected 已失败

状态改变流程

  • pending -> fulfilled
  • pending -> rejected

注意

  1. 不受外界影响
  2. 一旦改变无法再改变

基本功能

  1. 创建promise对象

    let p = new Promise((resolve,reject)=>{
        resolve('状态成功')
    })
    // 状态成功
    
  2. 创建promise对象状态只能改变一次

    let p = new Promise((resolve,reject)=>{
        resolve('状态成功')
        reject('状态失败')
    })
    // 状态成功
    
  3. 创建的promise对象可以调用then方法

    let p = new Promise((resolve,reject)=>{
        resolve('状态成功')
    })
    p.then(res=>{
        console.log(res,'then成功1');
    },err=>{
        console.log(err,'then失败1');
    })
    p.then(res=>{
        console.log(res,'then成功2');
    },err=>{
        console.log(err,'then失败2');
    })
    p.then(res=>{
        console.log(res,'then成功3');
    },err=>{
        console.log(err,'then失败3');
    })
    // 状态成功 then成功1
    // 状态成功 then成功2
    // 状态成功 then成功3
    
  4. 创建的promise对象(异步)也可以调用then方法

    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },1000)
    })
    p.then(res=>{
        console.log(res,'then成功1');
    },err=>{
        console.log(err,'then失败1');
    })
    // 状态成功 then成功1
    
  5. promise-resolve执行过程是一个异步任务

    let p = new Promise((resolve, reject) => {
        console.log('同步1');
        resolve('状态成功');
        console.log('同步2');
    })
    console.log('同步3');
    p.then(res => {
        console.log(res, 'then成功');
    }, err => {
        console.log(err, 'then失败');
    })
    console.log('同步4');
    
  6. 创建的promise对象在不同时机都可以调用then方法

    // 1.异步请求完毕之后注册成功、失败回调函数 
    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },1000)
    })
    setTimeout(()=>{
    	p.then(res=>{
            console.log(res,'then成功');
        },err=>{
            console.log(err,'then失败');
        })
    },2000)
    // 状态成功 then成功
    
    // 2.异步请求完毕之前注册成功、失败回调函数
    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },2000)
    })
    setTimeout(()=>{
    	p.then(res=>{
            console.log(res,'then成功');
        },err=>{
            console.log(err,'then失败');
        })
    },1000)
    // 状态成功 then成功
    
  7. 创建的promise对象可以链式调用then方法1(返回undefind、普通值)

    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },1000)
    })
    p.then(res=>{
        console.log(res,'then成功1');
    },err=>{
        console.log(err,'then失败1');
    }).then(res=>{
        console.log(res,'then成功2');
    },err=>{
        console.log(err,'then失败2');
    }).then(res=>{
        console.log(res,'then成功3');
    },err=>{
        console.log(err,'then失败3');
    })
    // 状态成功 then成功1
    // undefined then成功2
    // undefined then成功3
    
  8. 创建的promise对象可以链式调用then方法2(返回promise实例)

    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功1')
        },1000)
    })
    p.then(res=>{
        console.log(res,'then成功1');
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve('状态成功2')
            },1000)
        })
    },err=>{
        console.log(err,'then失败1');
    }).then(res=>{
        console.log(res,'then成功2');
    },err=>{
        console.log(err,'then失败2');
    }).then(res=>{
        console.log(res,'then成功3');
    },err=>{
        console.log(err,'then失败3');
    })
    // 状态成功 then成功1
    // 状态成功2 then成功2
    // undefined then成功3
    
  9. 处理异常-then中禁止返回自己

    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },1000)
    })
    let p2 = p.then(res=>{
        console.log(res,'then成功1');
    	return p2
    },err=>{
        console.log(err,'then失败1');
    })
    p2.then(res=>{
        console.log(res,'then成功2');
    },err=>{
        console.log(err,'then失败2');
    })
    // 状态成功 then成功
    // 禁止返回自己,抛出错误promise的错误 'then失败2'
    
  10. 异常处理-then的成功回调函数参数为非函数

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('状态成功')
    }, 1000)
})
p.then('ok', err => {
    console.log(err, 'then失败1');
}).then(()=>{
    console.log(res,'then成功2')
}, err => {
    console.log(err, 'then失败2');
}).then(res => {
    console.log(res, 'then成功3');
}, err => {
    console.log(err, 'then失败3');
})
// 状态成功 then成功2
// undefined 'then成功3'
  1. catch可以捕获之前所有异常

    let p = new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('状态成功')
        },1000)
    })
    p.then(res=>{
        console.log(res,'then成功1');
        throw '抛出报错'
    }).then(res=>{
        console.log(res,'then成功2');
    }).then(res=>{
        console.log(res,'then成功3');
    }).catch(err=>{
        console.log(err,'catch失败-抓取之前所有错误,并继续执行');
    }).then(res=>{
        console.log(res,'then成功4');
    })
    // 状态成功 then成功1
    // 抛出报错 catch失败-抓取之前所有错误,并继续执行
    // undefined 'then成功4'
    
  2. 快捷返回一个成功的promise实例

    let p = Promise.resolve('状态成功');
    p.then(res=>{
        console.log(res,'then成功');
    },err=>{
        console.log(err,'then失败');
    })
    // 状态成功 then成功
    
  3. 快捷返回一个失败的promise实例

    let p = Promise.reject('状态失败');
    p.then(res=>{
        console.log(res,'then成功');
    },err=>{
        console.log(err,'then失败');
    })
    // 状态失败 then失败
    
  4. 处理所有promise实例 all 方法

    let p = Promise.all([p1,p2,p3,p4])
    p.then(res=>{
        console.log(res,'then成功');
    },err=>{
        console.log(err,'then失败');
    })
    // [1结果,2结果,3结果,4结果] then成功
    
  5. 获取所有promise实例中最快的 race 方法

    let p = Promise.race([p1,p2,p3,p4])
    p.then(res=>{
        console.log(res,'then成功');
    },err=>{
        console.log(err,'then失败');
    })
    // 1、2、3、4中最快的结果 then成功
    

实现过程和代码

// 准备常量,防止使用过程中被改动
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
  constructor(executor) {
    // promiseA+ 规定 executor 执行器内置 resolve和reject
    // 记录状态
    this.status = PENDING;
    // 记录成功返回值
    this.value = undefined;
    // 记录失败返回值
    this.reason = undefined;
    // promise为异步时,status为pending等待结果,需要先记录then里面的成功回调函数,等待结果出来后一起执行
    this.onFulfilledCallbacks = [];
    // promise为异步时,status为pending等待结果,需要先记录then里面的失败回调函数,等待结果出来后一起执行
    this.onRejectedCallbacks = [];
    // 成功回调
    const resolve = (data) => {
      // 状态只能改变一次,如果状态改变过就不允许再次改变
      if (this.status !== PENDING) return;
      // 如果成功,改变状态和返回值
      this.status = FULFILLED;
      this.value = data;
      // 如果之前保存了then里面的回调,这里遍历数组执行里面的函数
      // 保证以下执行顺序为1、3、2,必须使用回调函数
      // 1、3是先放到数组中同步执行,等待结果同步执行,2是异步等1出来结果后再执行
      // p.then(res => {
      //   
      // })
      // .then(res => {
      //     
      // })
      // p.then(res => {
      //     
      // })
      if (this.onFulfilledCallbacks.length <= 0) return
      // 这里的异步放在push函数处效果一致
      // 新问题1,这里的回调好像没有意义,同理失败回调也是一样
      // 问题详情:new Promise中已经是异步,所以这里回调没必要是异步,只有同步的时候才会出现需要包裹异步的问题
      setTimeout(() => {
        this.onFulfilledCallbacks.forEach((fn) => fn());
      })
    };
    // 失败回调
    const reject = (data) => {
      if (this.status !== PENDING) return;
      // 如果失败,改变状态和返回值
      this.status = REJECTED;
      this.reason = data;
      if (this.onRejectedCallbacks.length <= 0) return
      setTimeout(() => {
        this.onRejectedCallbacks.forEach((fn) => fn());
      })
    };
    // 防止new Promise执行过程中就报错
    try {
      executor(resolve, reject);
    }
    catch (err) {
      reject(err)
    }
  }
  then(onFulfilled, onRejected) {
    // then的步骤三:then的异常/特殊处理
    // 需提前理解:
    // 上一个then执行的成功或者失败的结果并不影响下一个then的走向,只有返回的结果才影响后续then的走向
    // 3.1 如果返回值为普通值、undefined(没填)等 后续走成功
    // 3.2 如果返回值为异常、Promose.reject() 后续走失败
    // 1. 
    // 情况:正常流程,遇到报错时,then又缺少失败回调,利用catch抓取之前所有的错误
    // 代码:then(() => { }).then(() => { }).catch() 相当于 .then(() => { }, undefined).then(() => { }, undefined).catch()
    // 解释:如果报错,then中没有失败回调then(() => { },undefined),会导致失败回调返回 undefined ,返回值为undefined其实又会走回成功回调,导致出现问题,所以要保证在没有填写失败回调的时候,依然能够继续走失败
    // 2.
    // 情况:调用 .catch回调,本质也是调用 .then()。当想catch捕获错误,但前面没有错误时,catch的then就会走到then(undefined,()=>{})的undefined上,最终走到下一个成功,但值也要传递过去
    // 代码:.then(res=>{ return 没有错误都成功了 }).catch(()=>{}) 相当于 .then(res=>{ return 没有错误都成功了 }).then(undefined,() => { }) 
    // 解释:原本已经成功,但是需要把上一个值,传递到下一个then中,undefined无法传递,所以要有变量接收,再return返回才行
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };

    let promise2 = new Promise((resolve, reject) => {
      // then的步骤二:promise需要链式编程,then的返回值可以调用then,所以每一个then都必须返回一个新的promise实例,才可以调用then方法
      // then().then()
      // 进入then后,执行new promise也是同步,不用担心会影响内部3种状态的执行顺序
      // 链式调用then的时候,后面一个then的成功或失败,取决于上一个then的执行结果,重要:与上一个执行的成功回调或者失败回调无关,只跟return的内容有关
      // 2.1 如果返回的是 undefined(等于没有返回值)、数字、字符串都是代表成功
      // 2.2 如果返回的是 throw reason 和 Pormise.reject 都代表是失败
      // onFulfilled函数(上一个then的回调函数),需要得到他的结果,才可以触发resolve函数(下一个then的回调开关)
      // 所以 x = onFulfilled 的结果x,是决定resolve的关键
      // 但是x也许有很多情况,所以也要分类,接 resolvePromise 的封装

      // then的步骤一:进入then的时候status的分3种状态
      // 1.1 status状态1:之前new promise是同步,promise已经有结果,状态已经改变为fulfilled
      if (this.status === FULFILLED) {
        // promise 在调用then函数的时候是同步,但在执行then里面的成功、失败回调的时候是异步,所以加上定时器(防止promise2(同步) 未生成时就和x进行比较)
        setTimeout(() => {
          // onFulfilled(this.value);
          // then步骤四:封装
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err);
          }
        });
      }
      // 1.2 status状态2:之前new promise是同步,promise已经有结果,状态已经改变为rejected
      if (this.status === REJECTED) {
        setTimeout(() => {
          // onRejected(this.reason);
          // 封装
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err);
          }
        });

      }
      // 1.3 status状态3:之前promise是异步,promise还未有结果,状态还未改变(pending)。
      if (this.status === PENDING) {
        // 保存成功回调:此时promise还没有结果,所以可以先把then里面的成功回调都收集起来,等待状态改变后一起执行
        this.onFulfilledCallbacks.push(() => {
          // 这里没有直接push then里面的回调函数,而是在外面额外包裹了一层函数,是因为可以将参数一并保存起来
          // onFulfilled(this.value)
          // 封装
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err);
          }
        });
        // 保存失败回调
        this.onRejectedCallbacks.push(() => {
          // onRejected(this.reason)
          // 封装
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject)
          } catch (err) {
            reject(err);
          }
        });
      }
    });
    return promise2;
  }
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
  static reject = (reason) => {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static resolve = (value) => {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) { 
        value.then(resolve,reject)
      } else {
        resolve(value)
      }      
    })
  }
  static all = (promiseArr) => {
    return new Promise((resolve, reject) => {
      let count = 0;
      let resArr = [];      
      promiseArr.forEach((item,index) => { 
        item.then(res => {
          resArr[index] = res;
          count++
          if (count === promiseArr.length) {
            resolve(resArr)
          }
        }, err => {
          reject(err)
        })
      })
    })
  }
  static race = (promiseArr) => {
    console.log(promiseArr);
    return new Promise((resolve, reject) => {
      for (let p of promiseArr) {
        p.then(resolve,reject)
      }
    })
  }
}
const resolvePromise = (promise2, x, resolve, reject) => {
  // 该方法的核心运行得到x的结果,再根据x的结果驱动后面链式的then方法中的成功或失败回调
  // 1. 如果返回promise2自己:抛出错误。
  // 2. 如果返回promise对象(写法2:包括其他可能是promise结构的函数、对象等)
  // 2.1 如果返回可能是promise对象,有then方法,那么promise本身也要得到结果才可以驱动then中的new promise方法,再得到结果,驱动后面链式的then方法,所以多一步
  // 2.2 如果返回可能是promise对象,没then方法,把他当普通对象处理:直接触发resolve,触发后续的then方法
  // 3. 如果返回普通值,直接触发resolve方法,触发后续的then方法
  // 4. 如果报错,那么直接调用reject方法,触发后续的then方法

  // x状态的1:是promise2自己。处理循环调用,并不是死循环,只是防止promise会返回自己
  if (x === promise2) {
    // 这里不仅抛出错误,还要return阻止代码继续运行(原生抛出错误,自己封装不抛出错误,推测:自己封装抛出的错误被reject捕获、原生的错误是被浏览器识别报出)
    return reject(new TypeError('禁止返回自己'))
  }
  // //实现版本一:简单版开始
  // // 2. x的状态2:是promise对象
  // if (x instanceof Promise) {
  //   // 正常写法:
  //   // x.then(data => {
  //   //   resolve(data)
  //   // }, err => {
  //   //   reject(err)
  //   // })
  //   // 简写:
  //   // x.then(resolve, reject);
  //   // 理解1:
  //   // const fn = data =>resolve(data)
  //   // x.then(fn)
  //   // fn是下一个then里面的resolve回调函数,本身带了 res=>resolve(res)
  //   // 理解2:
  //   // const fn = data => { resolve(data) }; ele.onclick = () => { fn(data) }
  //   // ele.onclick = fn;
  //   // fn就是整个函数体,里面包含了参数。这里fn 就是 resolve函数体,等于直接把函数体传入。
  //   // 这里应该还少调用一层,成功或失败之后,再调用里面的resolve、reject函数
  // } else {
  //   // 3. x的状态3:是普通值
  //   resolve(x);
  // }
  // //实现版本一:简单版结束

  // 实现版本二:正规写法  
  // 2. x的状态2:promise对象,包括其他可能是promise结构的函数、对象等(普通对象、普通函数,他们身上都可能有then方法,只要不是null都可以进)
  // 版本一版本二对比 x instanceof Promise只能判断是否为promise对象,不包括对象、函数中有有then的方法,考虑没那么全面
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 问题1:called开关的作用是什么?而且必须要!
    // 自己理解:是防止promise如果走了成功或者失败回调后,突然报错,又走向catch的reject?到底是防止哪些多次回调??流程是什么?
    let called;
    try {
      let then = x.then;
      // 2.1 返回可能是promise对象,有then方法
      if (typeof then === 'function') {
        // 问题2:为什么必须要then.call,而不直接调x.then!防止函数内的this指向其他吗?
        // 问题3:原生返回promise实例,在内部会 p2.then().then() 处理两次,我代码只p2.then() 处理一次处理
        // 效果正常,检测报错 x.then().then(resolve,reject)
        // 检测正常,运行出错 如下代码:
        then.call(x, y => {
          if (called) return
          called = true;
          // 这里是防止promise实例嵌套,返回的还是promise对象,多层嵌套
          resolvePromise(promise2, y, resolve, reject)
        }, err => {
          if (called) return
          called = true;
          reject(err)
        })
      } else {
        // 2.2 返回可能是promise对象,没then方法
        resolve(x)
      }
    } catch (error) {
      // 4. 报错
      if (called) return
      called = true;
      reject(error)
    }
  } else {
    // 3. 返回普通值
    resolve(x)
  }
};

Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd
}
// module.exports = Promise

以上代码可以正常运行(静态方法不是特别严谨)并且可以通过检测,但其中有几个地方的思路未理顺(实现代码中出现了3个问题),如果有老大阅读,欢迎指定,谢谢

实现过程中的注意事项

  1. .then(成功回调,失败回调) 中的失败回调很少用,因为他只能捕获上一个promise实例的错误,而使用.catch() 可以捕获之前所有promise实例和函数内部错误
  2. new Promise中的resolve和reject为同步时, 会异步执行then里面的回调函数
    new Promise中的resolve和reject为异步时, 会同步把then里面的回调函数保存到数组中,再异步执行数组内容
  3. then() 的执行是同步,但.then(成功回调,失败回调)里面的回调函数是异步的(1.默认机制 2.因为then里面需要返回 retrun new Promise的实例,必须等他new完,所以这里有一个定时器)
  4. .then()里面的回调函数,成功和失败的执行,只取决于上一个then里面回调的 返回值(跟之前是成功回调还是失败的回调没关系)
    后续执行成功回调的返回值为:undefined、数字、字符串都是代表
    后续执行失败回调的返回值为:throw reason 和 Pormise.reject 都是代表
  5. promise 一般会在失败的最后加一个.catch() 来捕获之前所有的错误,这并不是一下子跳转过来的,而是逐级执行错误回调。这叫穿透(reason=> throw reason 或者 return Promise.reject(reason) 抛错误或者给一个失败回调)
  6. 需要中断promise,可以返回一个pending状态的promise实例
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值