当前位置: 首页 > news >正文

手写Promise

Promise

  • 为什么要有Promise?
  • Promise的实现
    • 功能简述

参考博客:
1.zhangpaopao0609
2.齐小神
视频推荐

为什么要有Promise?

为了解决回调地狱问题,也就是函数嵌套函数的问题。例子:
在这里插入图片描述

Promise的实现

Promise 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise是基于Promises/A+规范实现的,可以通过此规范的步骤自己手动实现一个Promise

功能简述

  1. Promise是一个构造函数,new Promise 时传入一个执行函数,并且执行函数是立即执行的
  2. 执行函数接收两个参数 resolve 函数 和 reject 函数,并且均能够接收参数
  3. Promise 的实例上有一个 then 方法, then 方法接收两个参数
  4. 进一步的,promise 有三个状态待定(pending),兑现(fulfilled),拒绝(rejected)。只能从pending到rejected, 或者从pending到fulfilled,状态一旦确认,就不会再改变。通过当前状态判断是否执行成功
  5. 然后就是解决传入promise是异步的问题
  6. 解决.then的链式调用,值的穿透

具体实现代码

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

const resolvePromise = (promise2, x, resolve, reject) => {
  // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1
  if (promise2 === x) { 
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // Promise/A+ 2.3.3.3.3 只能调用一次
  let called;
  // 后续的条件要严格判断 保证代码能和别的库一起使用
  if ((typeof x === 'object' && x != null) || typeof x === 'function') { 
    try {
      // 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候)  Promise/A+ 2.3.3.1
      let then = x.then;
      if (typeof then === 'function') { 
        // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
        then.call(x, y => { // 根据 promise 的状态决定是成功还是失败
          if (called) return;
          called = true;
          // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
          resolvePromise(promise2, y, resolve, reject); 
        }, r => {
          // 只要失败就失败 Promise/A+ 2.3.3.3.2
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      // Promise/A+ 2.3.3.2
      if (called) return;
      called = true;
      reject(e)
    }
  } else {
    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4  
    resolve(x)
  }
}

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks= [];

    let resolve = (value) => {
      if(this.status ===  PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    } 

    let reject = (reason) => {
      if(this.status ===  PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    }

    try {
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    //解决 onFufilled,onRejected 没有传值的问题
    //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后 then 的 resolve 中捕获
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    // 每次调用 then 都返回一个新的 promise  Promise/A+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        //Promise/A+ 2.2.2
        //Promise/A+ 2.2.4 --- setTimeout
        setTimeout(() => {
          try {
            //Promise/A+ 2.2.7.1
            let x = onFulfilled(this.value);
            // x可能是一个proimise
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            //Promise/A+ 2.2.7.2
            reject(e)
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        //Promise/A+ 2.2.3
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e)
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(()=> {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

相关文章:

  • Automatic differentiation package - torch.autograd
  • 第8章Linux实操篇-实用指令
  • net基于asp.net的社区团购网站-计算机毕业设计
  • 山石网科等级保护培训笔记
  • JavaScript获取文件的file对象数据(不通过input)
  • 数据结构与算法 -- 动态规划常见问题
  • 3.ICMP
  • 尚好房 09_权限管理
  • java面向对象(一)
  • Allegro Design Entry HDL(OrCAD Capture HDL)工具菜单详细介绍
  • Java 线程及线程池的创建方式
  • 分布式ID生成服务
  • Vue中的条件渲染v-if、v-show
  • 【Spring Boot】响应JSON实现原理
  • 基于51单片机交通信号灯仿真_东西管制+南北管制
  • ----------
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【Amaple教程】5. 插件
  • Android单元测试 - 几个重要问题
  • Bootstrap JS插件Alert源码分析
  • gf框架之分页模块(五) - 自定义分页
  • JavaScript 基本功--面试宝典
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • nginx 配置多 域名 + 多 https
  • react-native 安卓真机环境搭建
  • Redux系列x:源码分析
  • spring boot下thymeleaf全局静态变量配置
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 排序(1):冒泡排序
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 如何胜任知名企业的商业数据分析师?
  • 如何学习JavaEE,项目又该如何做?
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 使用Gradle第一次构建Java程序
  • 通过几道题目学习二叉搜索树
  • 我与Jetbrains的这些年
  • 项目管理碎碎念系列之一:干系人管理
  • 用jQuery怎么做到前后端分离
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 正则学习笔记
  • 组复制官方翻译九、Group Replication Technical Details
  • !$boo在php中什么意思,php前戏
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (二十四)Flask之flask-session组件
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)http协议
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • ./configure、make、make install 命令
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .form文件_SSM框架文件上传篇
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET MVC第三章、三种传值方式