假如面试官要你手写一个promise

news/2024/7/5 10:14:59

promise

在开发中,经常需要用到promise,promise具有很多特性,这一次将对promise特性进行总结,并从零写一个promise。

步骤一

  • Promise特点
    • 1,创建时需要传递一个函数,否则会报错
    • 2,会给传入的函数设置两个回调函数
    • 3,刚创建的Promise对象状态是pending
class MyPromise {
  constructor(handle) {
    // 3,刚创建的Promise对象状态是pending
    this.status = "pending";
    // 1,创建时需要传递一个函数,否则会报错
    if (!this._isFunction(handle)) {
      throw new Error("请传入一个函数");
    }
    // 2,会给传入的函数设置两个回调函数
    handle(this._resolve.bind(this), this._reject.bind(this))
  }
  _resolve() {

  }
  _reject() {

  }
  _isFunction(fn) {
    return typeof fn === "function";
  }
}

步骤二

  • Promise特点
    • 4,状态一旦发生改变就不可再次改变
    • 5,可以通过then来监听状态的改变
      • 5.1,如果创建监听时,状态已经改变,立即执行监听回调
      • 5.2,如果创建监听时,状态未改变,会等状态改变后执行
      • 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行
// 定义常量保存对象的状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  constructor(handle) {
    // 3,刚创建的Promise对象状态是pending
    this.status = PENDING;
    // 成功回调的值
    this.value = undefined;
    // 失败回调的值
    this.reason = undefined;
    // 注册的成功回调
    this.onResolvedCallbacks = [];
    // 注册的失败回调
    this.onRejectedCallbacks = [];
    // 1,创建时需要传递一个函数,否则会报错
    if (!this._isFunction(handle)) {
      throw new Error("请传入一个函数");
    }
    // 2,会给传入的函数设置两个回调函数
    handle(this._resolve.bind(this), this._reject.bind(this))
  }
  _resolve(value) {
    // 4,状态一旦发生改变就不可再次改变
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      // 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行
      this.onResolvedCallbacks.forEach(fn => fn(this.value));
    }
  }
  _reject(reason) {
    // 4,状态一旦发生改变就不可再次改变
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      // 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行
      this.onRejectedCallbacks.forEach(fn => fn(this.reason));
    }
  }
  then(onResolved, onRejected) {
    // 判断有没有传入成功的回调
    if (this._isFunction(onResolved)) {
      // 5.1,如果创建监听时,状态已经改变,立即执行监听回调
      if (this.status === FULFILLED) {
        onResolved(this.value);
      }
    }
    // 判断有没有传入失败的回调
    if (this._isFunction(onRejected)) {
      // 5.1,如果创建监听时,状态已经改变,立即执行监听回调
      if (this.status === REJECTED) {
        onRejected(this.reason);
      }
    }
    // 5.2,如果创建监听时,状态未改变,会等状态改变后执行
    if (this.status === PENDING) {
      if (this._isFunction(onResolved)) {
        this.onResolvedCallbacks.push(onResolved);
      }
      if (this._isFunction(onRejected)) {
        this.onRejectedCallbacks.push(onRejected);
      }
    }
  }
  _isFunction(fn) {
    return typeof fn === "function";
  }
}

参考 前端手写面试题详细解答

详解then方法

  • 接收两个参数:成功回调,失败回调
  • 如果promise失败了,但是没有注册失败监听,就会报错
  • then方法每次执行完毕都会返回一个新的Promise对象
    • 如果then方法只有成功回调
      • 则它返回的promise的状态会继承当前promise的状态。
      • 如果当前promise的状态为成功:新promise的值为当前then的成功回调的返回值。
      • 如果当前promise的状态为失败:新的promise没有失败监听,则会报错
    • 如果then方法同时包含成功回调、失败回调
    • 则它返回的promise的状态都为成功,且值为成功或者失败回调的返回值。
  • 回调函数的返回值
    • 如果then方法的成功/失败回调返回的是promise对象
      • 则then方法返回的新的promise对象的状态由新promise的内部决定。
      • 且值为新promise的内resolve/reject函数传递的参数。
    • 如果then方法的成功/失败回调返回的是普通数据类型
      • 则then方法返回的新的promise对象的状态都为成功。
      • 且值为成功/失败回调的返回值,即都会传递给新的promise对象成功的回调。
    • 如果then方法的成功/失败回调没有返回值
      • 同返回普通数据类型
  • 失败回调函数
    • 可以捕获上一个promise对象的then方法中成功回调函数执行时的异常
then(onResolved, onRejected) {
    return new MyPromise((nextResolve, nextReject) => {
      // 1.判断有没有传入成功的回调
      if (this._isFunction(onResolved)) {
        // 2.判断当前的状态是否是成功状态
        if (this.status === FULFILLED) {
          try {
            // 拿到上一个promise成功回调执行的结果
            let result = onResolved(this.value);
            // console.log("result", result);
            // 判断执行的结果是否是一个promise对象
            if (result instanceof MyPromise) {
              result.then(nextResolve, nextReject);
            } else {
              // 将上一个promise成功回调执行的结果传递给下一个promise成功的回调
              nextResolve(result);
            }
          } catch (e) {
            nextReject(e);
          }
        }
      }
      // 1.判断有没有传入失败的回调
      // if(this._isFunction(onRejected)){
      try {
        // 2.判断当前的状态是否是失败状态
        if (this.status === REJECTED) {
          let result = onRejected(this.reason);
          if (result instanceof MyPromise) {
            result.then(nextResolve, nextReject);
          } else {
            nextResolve(result);
          }
        }
      } catch (e) {
        nextReject(e);
      }
      // }
      // 2.判断当前的状态是否是默认状态
      if (this.status === PENDING) {
        if (this._isFunction(onResolved)) {
          // this.onResolvedCallback = onResolved;
          this.onResolvedCallbacks.push(() => {
            try {
              let result = onResolved(this.value);
              if (result instanceof MyPromise) {
                result.then(nextResolve, nextReject);
              } else {
                nextResolve(result);
              }
            } catch (e) {
              nextReject(e);
            }
          });
        }
        // if(this._isFunction(onRejected)){
        // this.onRejectedCallback = onRejected;
        this.onRejectedCallbacks.push(() => {
          try {
            let result = onRejected(this.reason);
            if (result instanceof MyPromise) {
              result.then(nextResolve, nextReject);
            } else {
              nextResolve(result);
              nextReject();
            }
          } catch (e) {
            nextReject(e);
          }
        });
        // }
      }
    });
}

详解catch方法

  • 其实是then方法的失败回调函数的语法糖
  • 如果需要同时使用then和catch方法,必须使用链式编程,不然会报错
  • 可以捕获上一个promise对象的then方法中成功回调函数执行时的异常
catch(onRejected) {
    return this.then(undefined, onRejected);
}

为啥使用catch时最好使用链式编程

  • 因为then方法只有成功回调,所以p2的状态会继承p1
  • 又因为p2的状态为失败,且没有对p2进行失败监听,所以报错
let p1 = new Promise(function (resolve, reject) {
  // resolve();
  reject();
});
let p2 = p1.then(function () {
  console.log("成功");
});
p1.catch(function () {
  console.log("失败1");
});

Promise.all()

  • Promise.all(params)特点
    • 参数为一个数组,且数组元素为promise类型数据
    • 返回值为一个promise,
      • 如果所有promise都执行成功
        • 返回值为所有promise都成功时返回的结果的集合
      • 如果有一个promise执行失败了,则返回失败的promise
static all(list){
    return new MyPromise(function (resolve, reject) {
        let arr = [];
        let count = 0;
        for(let i = 0; i < list.length; i++){
            let p = list[i];
            p.then(function (value) {
                // arr.push(value); 注意不要这样写,会导致结果顺序不对
                arr[i] = value
                count++;
                if(list.length === count){
                    resolve(arr);
                }
            }).catch(function (e) {
                reject(e);
            });
        }
    });
}

Promise.race()

  • Promise.race(params)特点
    • 参数为一个数组,且数组元素为promise类型数据
    • 返回值为一个promise,且返回值为第一个成功或者失败的promise的值
static race(list){
    return new MyPromise(function (resolve, reject) {
        for(let p of list){
            p.then(function (value) {
                resolve(value);
            }).catch(function (e) {
                reject(e);
            });
        }
    })
}


http://lihuaxi.xjx100.cn/news/58078.html

相关文章

《测绘综合能力》——大地测量

欢迎访问《注册测绘师考试知识》系列。《测绘综合能力》 大地测量 部分重要(常考)知识点(精简版)如下: 目录 1 大地测量参考系统与框架2 大地测量常数3 建立参心坐标系4 建立地心坐标系5 高程基准6 正常高7 深度基准8 重力系统与框架9 时间系统与框架10 三差改正(地面→椭…

gamma校正

伽玛校正&#xff08;Gamma Correction&#xff09;校正的目的输入转至线性空间输出前进行校正衰减校正的目的 保证所有的输入都转换到线性空间&#xff0c;并在线性空间下做各种光照计算&#xff08;线性空间进行操作&#xff09;&#xff0c;最后输出通过gamma校正后进行显示…

vue2初学1-37

vue是什么 是一套用于构建用户界面&#xff08;将数据变成界面&#xff09;的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方面&…

笔试强训(十七)

目录一、选择题二、编程题2.1 杨辉三角的变形2.1.1 题目2.1.2 题解2.2 计算字符出现的次数2.2.1 题目2.2.2 题解一、选择题 &#xff08;1&#xff09;下列SQL语句中哪条语句可为用户zhangsan分配数据库userdb表userinfo的查询和插入数据权限&#xff08;A&#xff09; A.gran…

Linux学习 -- Shell学习之条件判断

常用的判断条件if判断case判断 一、常用的判断条件 1、基本语法 [ condition ]&#xff08;注意condition前后要有空格&#xff09;。 注意:条件非空即为 true&#xff0c;[ atguigu ]返回true&#xff0c;[返回false。 2&#xff0e;常用判断条件。 (1)两个整数之间比…

ASP.NET Core--配置文件

文章目录配置文件添加配置文件读取配置文件控制台读取配置文件**控制器中读取对象读取自定义配置文件多环境配置文件配置文件 添加配置文件 在项目目录下有个 appsettings.json &#xff0c;我们先来操作这个文件。 在appsettings.json 添加如下两个节点。 {"Data&quo…

2022-10-04 语法分析器bison说明

https://www.gnu.org/software/bison/manual/bison.html 参考: https://blog.csdn.net/weixin_44705391/article/details/115555161 https://zhuanlan.zhihu.com/p/52326306 https://zhuanlan.zhihu.com/p/120812270 https://zhuanlan.zhihu.com/p/89479111 说明: Unix Le…

【问题解决】大佬亲授的姿势——PlatformIO生成bin文件方法

微信关注公众号 “DLGG创客DIY”设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。最近写创客项目程序基本都用PlatformIO&#xff08;以后简称PIO&#xff09;&#xff0c;PIO在很多方面都优于arduino IDE&#xff0c;今天就不展开了啊&#xff0c;回头专门起一篇文…