手写promise

写在前面(核心逻辑)

1. Promise 是一个类, 在执行这个类的时候 需要传递一个执行器进去,执行器会立即执行
2. Promise 中有三种状态,成功=>fulfilled,失败=>rejected,等待=>pending
pending --> fulfilled
pending --> rejected
状态一旦确定就不可更改
3. resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事情就是判断状态,如果状态时成功,调用成功的回调函数,如果状态是失败,调用失败的回调函数,then方法是被定义在原型对象中的方法
5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
6. 同一个promise对象下面的then方法是可以被调用多次的
7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
8. catch只是then的一种特殊的写法 方便理解和使用
9. all 返回一个Promise,入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致, 所有的值或者promise都完成才能resolve 所有要计数 ,只要有一个为reject 返回的Promise便reject
10. race 返回一个Promise, 入参是数组 那么出参根据第一个成功或者失败的参数来确定, 只要有一个resolve 或者reject 便更改返回Promise的状态

// 状态定义成常量代码会有提示并且方便复用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {constructor(executor) {executor(this.resolve, this.reject);}// promise 状态status = PENDING;// 成功之后的值value = undefined;// 失败后的原因reason = undefined;// 箭头函数目的是为了让this指向类的实例对象resolve = (value) => {// 如果状态不是等待,阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为成功this.status = FULFILLED;// 保存成功之后的值this.value = value;};reject = (reason) => {// 如果状态不是等待,阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为失败this.status = REJECTED;// 保存失败之后的原因this.reason = reason;};then(successCallback, failCallback) {// 判断状态if (this.status === FULFILLED) {successCallback(this.value);} else if (this.status === REJECTED) {failCallback(this.reason);}}
}
  • 使用自定义的promise
let promise = new MyPromise((resolve, reject) => {resolve("成功");reject("失败");
});promise.then((res) => {console.log(res);},(err) => {console.log(err);}
);

在Promise类中加入异步逻辑

  • 需要在Promise类中加入以下代码

在类中定义两个回调函数

 // 成功回调successCallback = undefined;// 失败回调failCallback = undefined;

在resolve函数内加入

// 判断成功回调是否存在,如果存在调用,成功的值必须传入
this.successCallback && this.successCallback(this.value);

在reject函数内加入

// 判断失败回调是否存在,如果存在调用,失败的原因必须传入
this.failCallback && this.failCallback(this.reason);

最后在then函数内加入

 else {// 当前状态是等待this.successCallback = successCallback;this.failCallback = failCallback;}
  • 再次使用自定义的promise
let promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve("成功");}, 2000);reject("失败");
});

实现 then 方法多次调用添加多个处理函数

  • 收先将声明的成功回调和失败回调定义为数组
    // 成功回调successCallback = [];// 失败回调failCallback = [];
  • 然后在then的等待条件中(处理异步)将then的函数push到数组中
    // 当前状态是等待 异步状态存储then, then可以多个处理所以是个数组this.successCallback.push(successCallback);this.failCallback.push(failCallback);
  • 最后执行多个then中的函数,需要在resolve和reject函数内操作

将判断成功的回调改为循环数组的形式,每执行一次回调从数组中弹出

    // this.successCallback && this.successCallback(this.value);while (this.successCallback.length) {this.successCallback.shift()(this.value);}

失败的同理

    // this.failCallback && this.failCallback(this.reason);while (this.failCallback.length) {this.failCallback.shift()(this.reason);}
  • 最后执行 就会得到多个函数结果
let promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve("成功,有几个then就会有几个成功");}, 2000);
});
promise.then((res) => {console.log(res);},(err) => {console.log(err);}
);
promise.then((res) => {console.log(res);},(err) => {console.log(err);}
);

实现 then 方法的链式调用

  • 实现then方法的链式调用,必须要让then方法自身返回一个promise对象,所以这里对then内部进行一个修改
  • 还需要对回调的返回值进行判断,判断返回值是普通值还是一个promise对象,从而进行区分处理
  • 同时将之前未处理的异常一并处理 捕获错误
    • 在执行器中捕获异常
    • 在成功/失败/等待内部进行捕获异常

定义一个判断成功回调值的类型

// 定义一个判断成功回调值的类型
function resolvePromise(promise2, x, resolve, reject) {// 如果then方法返回的promise和成功的回调返回的promise是同一个对象则程序报出类型错误并且不再向下进行if (promise2 === x) {return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));}// 判断如果是MyPromise的实例则调用then方法,根据传入的参数来判断是执行resolve还是rejectif (x instanceof MyPromise) {x.then(resolve, reject);} else {// 普通值resolve(x);}
}

then方法内经过处理后最新代码更改

     then(successCallback, failCallback) {// 实现then方法的链式调用,必须要让then方法自身返回一个promise对象let promise2 = new MyPromise((resolve, reject) => {// 统一异常处理逻辑const execFun = (fn, val) => {try {let x = fn(val);// 判断x的值是普通值还是promise对象// 1.如果是普通值 直接调用resolve// 2.如果是promise对象,查看promise对象的返回结果,再根据promise对象返回结果 决定调用resolve还是调用rejectresolvePromise(promise2, x, resolve, reject);} catch (e) {// 调用下一个promise的reject方法,将错误信息传入reject(e);}};// 判断状态if (this.status === FULFILLED) {// 内部无法直接拿到promise,所以需要将这一步操作变成异步操作,拿到promise2之后再调用queueMicrotask(() => {execFun(successCallback, this.value);});} else if (this.status === REJECTED) {queueMicrotask(() => {execFun(failCallback, this.reason);});} else {// 同步将对应成功或者失败回调事件加入对应回调队列, then可以多个处理所以是个数组,push到数组中一个函数,函数内部进行异步操作来调用相对应的成功/失败的回调this.successCallback.push(() => {execFun(successCallback, this.value);});this.failCallback.push(() => {execFun(failCallback, this.reason);});}});return promise2;}

执行器内部异常处理代码更改

constructor(executor) {// 立即执行传入参数// 异步执行状态变更// 捕获执行器的异常try {executor((value) => {queueMicrotask(() => {this.resolve(value);});},(reason) => {queueMicrotask(() => {this.reject(reason);});});} catch (error) {this.reject(error);}}

判断成功/失败的回调代码更改—不需要这里传值,只需要执行即可(因为内部push的是一个函数,函数内部进行了赋值操作)

    // 判断成功回调是否存在,如果存在调用,成功的值必须传入while (this.successCallback.length) {this.successCallback.shift()();}
    // 判断失败回调是否存在,如果存在调用,失败的原因必须传入while (this.failCallback.length) {this.failCallback.shift()();}

将 then 方法的参数变成可选参数

执行调用相当于外部传入一个参数和返回值相同的函数

let promise = new MyPromise((resolve, reject) => {resolve(100);
});
  • 加入代码前
promise.then((value) => value).then((value) => value).then((res) => console.log(res));
  • 在then方法中第一句加入以下代码表示

    • 如果then方法中没有传入函数,那么需要将值一直向后传递,一直传递给有回调函数的then方法(参数必须是函数)
    successCallback = typeof successCallback === "function" ? successCallback : (value) => value;// 抛出一个reason表达式的异常failCallback = typeof failCallback === "function" ? failCallback : (reason) => { throw reason };
  • 加入代码后即可实现无参数调用then,将值一直向后传递
promise.then().then().then((res) => console.log(res));

Promise.all方法的实现

  • all是Promise的静态方法
  • all方法是解决异步并发问题的,允许按照异步代码调用的顺序得到异步调用的结果
  • all方法的返回值也是一个promise对象
  • 所有的成功才成功,只要有一个失败整个all就失败

all方法实现代码如下

    static all(array) {let result = [];let index = 0;return new MyPromise((resolve, reject) => {function addData(key, value) {result[key] = value;index++;// 因为for循环会很快结束,这一步代码主要是等待异步操作完成之后才resolveif (index === array.length) {resolve(result);}}for (let i = 0; i < array.length; i++) {// 保存当前值let current = array[i];if (current instanceof MyPromise) {// promise对象current.then((value) => {addData(i, value);},(reason) => {reject(reason);});} else {// 普通值addData(i, array[i]);}}});}
  • 执行代码
function p1() {return new MyPromise((resolve, reject) => {setTimeout(() => {resolve("p1");}, 2000);});
}
function p2() {return new MyPromise((resolve, reject) => {resolve("p2");});
}MyPromise.all(["a", "b", p1(), p2(), "c"]).then((res) => {console.log(res);
});

race方法的实现

  • race是静态方法
  • 和all不同的是只要有一个成功或者失败就返回
    static race(array) {return new Promise((resolve, reject) => {for (let i = 0; i < array.length; i++) {let current = array[i];// 如果是MyPromise实例if (current instanceof MyPromise) {current.then(resolve, reject);} else {// 普通值resolve(current);}}});}
  • 执行代码
MyPromise.race([p2(), p1(),'a', "b", "c"]).then((res) => {console.log(res,1);}).catch((err) => {console.log(err,2);});// 最先输出a 1 但是会等到p2内部的setTimeout结束程序才会结束

resolve方法的实现

  • resolve是静态方法,作用是将传入参数转变为promise对象

    • 在类中添加一个静态方法,代码如下
    static resolve(value) {// 如果是promise对象则直接返回if (value instanceof MyPromise) return value;// 如果是普通值则创建一个promise对象返回return new MyPromise((resolve) => resolve(value));}

finally方法的实现

  • finally无论成功还是失败callback都会执行,
  • 返回当前promise对象最终返回的结果
finally(callback) {return this.then((value) => {return MyPromise.resolve(callback()).then(() => value);},(reason) => {return MyPromise.resolve(callback()).then(() => {throw reason;});});}

catch方法的实现

  • catch 方法用来处理当前promise对象最终状态为失败的情况(直接调用then方法即可)
    catch(failCallback) {// 内部也是调用的then方法,只不过没有成功的回调return this.then(undefined, failCallback);}

最终promise代码

// 状态定义成常量代码会有提示并且方便复用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {constructor(executor) {// 立即执行传入参数// 异步执行状态变更// 捕获执行器的异常try {executor((value) => {queueMicrotask(() => {this.resolve(value);});},(reason) => {queueMicrotask(() => {this.reject(reason);});});} catch (error) {this.reject(error);}}// promise 状态status = PENDING;// 成功之后的值value = undefined;// 失败后的原因reason = undefined;// 成功回调successCallback = [];// 失败回调failCallback = [];// all是静态方法static all(array) {let result = [];let index = 0;return new MyPromise((resolve, reject) => {function addData(key, value) {result[key] = value;index++;// 因为for循环会很快结束,这一步代码主要是等待异步操作完成之后才resolveif (index === array.length) {resolve(result);}}for (let i = 0; i < array.length; i++) {let current = array[i];if (current instanceof MyPromise) {// promise对象current.then((value) => {addData(i, value);},(reason) => {reject(reason);});} else {// 普通值addData(i, current);}}});}// resolve是静态方法,作用是将传入参数转变为promise对象static resolve(value) {// 如果是promise对象则直接返回if (value instanceof MyPromise) return value;// 如果是普通值则创建一个promise对象返回return new MyPromise((resolve) => resolve(value));}// race 是静态方法 只要有一个成功或者失败就返回static race(array) {return new Promise((resolve, reject) => {for (let i = 0; i < array.length; i++) {let current = array[i];// 如果是MyPromise实例if (current instanceof MyPromise) {current.then(resolve, reject);} else {// 普通值resolve(current);}}});}// 箭头函数目的是为了让this指向类的实例对象resolve = (value) => {// 如果状态不是等待,阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为成功this.status = FULFILLED;// 保存成功之后的值this.value = value;// 判断成功回调是否存在,如果存在调用,成功的值必须传入while (this.successCallback.length) {this.successCallback.shift()();}};reject = (reason) => {// 如果状态不是等待,阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为失败this.status = REJECTED;// 保存失败之后的原因this.reason = reason;// 判断失败回调是否存在,如果存在调用,失败的原因必须传入while (this.failCallback.length) {this.failCallback.shift()();}};then(successCallback, failCallback) {// 如果then方法中没有传入函数,那么需要将值一直向后传递,一直传递给有回调函数的then方法successCallback =typeof successCallback === "function"? successCallback: (value) => value;failCallback =typeof failCallback === "function"? failCallback: (reason) => {// 抛出一个reason表达式的异常throw reason;};// 实现then方法的链式调用,必须要让then方法自身返回一个promise对象let promise2 = new MyPromise((resolve, reject) => {// 统一异常处理逻辑const execFun = (fn, val) => {try {let x = fn(val);// 判断x的值是普通值还是promise对象// 1.如果是普通值 直接调用resolve// 2.如果是promise对象,查看promise对象的返回结果,再根据promise对象返回结果 决定调用resolve还是调用rejectresolvePromise(promise2, x, resolve, reject);} catch (e) {// 调用下一个promise的reject方法,将错误信息传入reject(e);}};// 判断状态if (this.status === FULFILLED) {// 内部无法直接拿到promise,所以需要将这一步操作变成异步操作,拿到promise2之后再调用queueMicrotask(() => {execFun(successCallback, this.value);});} else if (this.status === REJECTED) {queueMicrotask(() => {execFun(failCallback, this.reason);});} else {// 同步将对应成功或者失败回调事件加入对应回调队列, then可以多个处理所以是个数组,push到数组中一个函数,函数内部进行异步操作来调用相对应的成功/失败的回调this.successCallback.push(() => {execFun(successCallback, this.value);});this.failCallback.push(() => {execFun(failCallback, this.reason);});}});return promise2;}// finally无论成功还是失败callback都会执行,并且返回当前promise对象最终返回的结果finally(callback) {return this.then((value) => {return MyPromise.resolve(callback()).then(() => value);},(reason) => {return MyPromise.resolve(callback()).then(() => {throw reason;});});}// catch 方法用来处理当前promise对象最终状态为失败的情况(直接调用then方法即可)catch(failCallback) {// 内部也是调用的then方法,只不过没有成功的回调return this.then(undefined, failCallback);}
}// 定义一个判断成功回调值的类型
function resolvePromise(promise2, x, resolve, reject) {// 如果then方法返回的promise和成功的回调返回的promise是同一个对象则程序报出类型错误并且不再向下进行if (promise2 === x) {return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));}// 判断如果是MyPromise的实例则调用then方法,根据传入的参数来判断是执行resolve还是rejectif (x instanceof MyPromise) {x.then(resolve, reject);} else {// 普通值resolve(x);}
}

一步一步的手写Promise相关推荐

  1. 面试必备--手写Promise.all与.race

    最近面试被问到了手写Promise .all 与 Promise.race,奈何没有自己实现过,只能阿巴阿巴 面完之后,冷静下来思考了该如何实现,并把他写了下来(在实现过程中确实收获不少,让我对这两个 ...

  2. 【学习笔记】Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程、手写 Promise(二、JavaScript 异步编程)

    [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程.手写 Promise(课前准备) [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步 ...

  3. Promise原理及手写Promise

    原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...

  4. javascript --- 手写Promise、快排、冒泡、单例模式+观察者模式

    手写promise 一种异步的解决方案, 参考 Promise代码基本结构 function Promise(executor){this.state = 'pending';this.value = ...

  5. 方法 手写promise_JS探索-手写Promise

    无意间在知乎上刷到Monad这个概念,去了解了一下,前端的Promise就是一种Monad模式,所以试着学习一下手写一个Promise. 本文内容主要参考于 只会用?一起来手写一个合乎规范的Promi ...

  6. 一个下课的时间带你手写promise!

    要手写前先看看用法,用法就是我们的需求 //直接调用 let promise=new Promise((resolve,reject)=>{resolve('123') }) promise.t ...

  7. 手写 Promise

    手写 Promise 实现一个简易版 Promise 在完成符合 Promise/A+ 规范的代码之前,我们可以先来实现一个简易版 Promise,因为在面试中,如果你能实现出一个简易版的 Promi ...

  8. 【Promise】自定义 - 手写Promise - Promise.all - Promise(executor)

    手写Promise 1. 整体结构框架 2. Promise(executor) 3. Promise.prototype.then 4. Promise.prototype.catch 5. Pro ...

  9. c0语言 测试用例,按照 Promise/A+ 手写Promise,通过promises-aplus-tests的全部872个测试用例...

    本文主要讲述如何根据 Promises/A+ 规范,一步步手写一个 Promise 的 polyfill,代码中会配上对应的规范解释. 1. 定义需要的常量和工具方法// 1. 定义表示promsie ...

  10. 手写Promise和all、race等方法,附上原理解析

    手写一个迷你版的Promise JavaScript 中的 Promise 诞生于 ES2015(ES6),是当下前端开发中特别流行的一种异步操作解决方案,简单实现一个迷你版本帮助深入理解 Promi ...

最新文章

  1. View Transform(视图变换)详解
  2. 5.QT5中的connect的实现
  3. 校园网站建设策划方案离不开三方面
  4. zabbix源码安装 令人窒息的操作
  5. XML学习笔记(二)-- DTD格式规范
  6. web前端实战系列[4]——多级菜单
  7. PyQt5学习笔记03----Qt Designer生成源码
  8. Excel导出改造_只填写字典类型_就可以自动对应导出_字典类添加获取字典值方法---SpringCloud Alibaba_若依微服务框架改造---工作笔记013
  9. 墨者学院——密码学加解密实训(Base64转义)
  10. 用php的ob_start()控制浏览器cache
  11. matlab算薄板模态,基于MATLAB计算FGM薄板刚柔耦合动力学响应的仿真方法与流程
  12. MySQL使用简单教程
  13. 神舟战神z7-ct7nt的键盘灯控制不了的解决
  14. 解决‘GNN’中‘over—smoothing’问题(通俗易懂)
  15. 定制MACD背离副图
  16. sed替换写法sed -e 's; ;:;g'
  17. 【欧拉计划第 3 题】最大质因数 Largest prime factor
  18. 4底2分化查找程序的作业树
  19. Swift基础(六)解包
  20. 物联网毕设 -- 基于STM32的心率检测

热门文章

  1. normalize.css v8.0.1中文版 - 官方最新
  2. 10月20日前!武汉市科技成果转化中试平台(基地)备案申报条件及流程梳理
  3. 翟菜花:《完美世界》手游大火,大文娱的卡路里能释放多少热量?
  4. BigDecimal实现加减乘除
  5. 按键短按、长按,双击
  6. CF1437F Emotional Fishermen
  7. shiro集成jwt
  8. 【Python】pandas的read_csv参数简略概括(header,path),DataFrame的返回值describe,plot,head
  9. java workbook.close_找不到符号Workbook.close()
  10. Hibernate 映射关系 ---One2One 主键关联