文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从零实现一个Promise

2024-12-10 16:47

关注

一、Promise/A+ 规范
① Promise 是一个类或者函数,内部拥有3个状态,分别为pending(等待)、fulfilled(执行、完成)、rejected(拒绝、未完成)。

默认为pending状态,即Promise对象刚创建的时候状态为pending,并且pending状态可以转换fulfilled或者rejected。

fulfilled和rejected为最终的状态,一旦变为fulfilled或者rejected,那么将无法转变为其他状态。

② Promise需要对外提供一个then方法。

  1. promise.then(onFulfilled, onRejected) 

如果可选参数onFulfilled和onRejected不为函数时应该被忽略;

onFulfilled和onRejected函数都应该是异步执行的;

当调用 onFulfilled 函数时,会将当前 Promise 的值作为参数传入,并且只能调用一次;

当调用 onRejected 函数时,会将当前 Promise 的失败原因作为参数传入,并且只能调用一次;

then函数的返回值仍然为Promise,以便进行链式调用;

③ resolvePromisethen方法会创建并返回一个Promise对象,then中注册的回调函数会返回各种值,必须进行校验。

then方法返回的promise不能与then中回调函数返回值x相等,否则需要抛出错误;

如果是then回调函数返回值为一个非Promise对象,则直接用then返回的promise对象的resolve方法,resolve(x)即可。

如果then回调函数返回值x为一个Promise对象或者一个带then方法的对象或函数,那么需要执行其then方法注册回调,拿到Promise或类Promise对象的值作为then返回的promise的值,如果值仍然为Promise对象则需要进行递归操作;

二、实现Promise
① 根据第一条规范,Promise是一个类或者函数,所以我们先将Promise定义成一个类,同时内部有三个状态,我们将其定义为常量。

  1. var PENDING = "pending"; // 等待状态 
  2. var FULFILLED = "fulfilled"; // 执行、完成状态 
  3. var REJECTED = "rejected"; // 拒绝、未完成状态 
  4. class Promise { 
  5.     constructor() { 
  6.         this.state = PENDING; // Promise对象创建完成后默认为等待状态 
  7.     } 

② 我们在创建Promise的时候会传入一个函数,该函数会在创建Promise对象的时候立即执行,并且会接收两个参数,分别用于执行或拒绝当前Promise对象,即修改当前Promise对象的状态。Promise是用于处理异步的,所以在Promise状态变为完成的时候可能会接收到异步操作执行的结果,在Promise状态变为未完成的时候可能会接收到失败的原因,所以Promise内部还需要保存异步操作的结果value、失败的原因reason。

 

  1. ...... 
  2. class Promise { 
  3.     constructor(executor) { // 传入执行器函数 
  4.         ...... 
  5.         this.value = undefined; // 保存异步操作的结果 
  6.         this.reason = undefined; // 保存失败的原因 
  7.         const resolve = (value) => { 
  8.             this.value = value; 
  9.             this.state = FULFILLED; // 将Promise对象的状态改为完成状态 
  10.         } 
  11.         const reject = (reason) => { 
  12.             this.reason = reason; 
  13.             this.state = REJECTED; // 将Promise对象的状态改为未完成状态 
  14.         } 
  15.         try { 
  16.             executor(resolve, reject); // 执行器由用户传入可能会发生错误,所以需要进行捕获 
  17.         } catch(e) { 
  18.             reject(e); 
  19.         } 
  20.     } 

③ 这里还存在一个问题,就是Promise必须是单次执行的,Promise的状态一旦从pending状态修改为fulfilled或者rejected,就不能再发生变化,从fulfilled变为fulfilled也不可以,也就是说resolve或者reject只能执行一次。所以我们需要对resolve和reject内部进行判断,如果状态已经变化了则不再执行了,如:

  1. ...... 
  2. class Promise { 
  3.     constructor(executor) { // 传入执行器函数 
  4.         ...... 
  5.         const resolve = (value) => { 
  6.             if (this.state === PENDING) { // 防止用户多次resolve,以第一次resolve为准 
  7.                 ...... 
  8.             } 
  9.         } 
  10.         const reject = (reason) => { 
  11.             if (this.state === PENDING) { // 防止用户多次reject 
  12.                 ...... 
  13.             } 
  14.         } 
  15.         ...... 
  16.     } 

④ 给Promise添加一个then函数,then函数接收onFulfilled, onRejected两个函数作为参数,分别用于处理Promise完成时和未完成时的回调函数,如果不是函数,则要进行初始化为一个函数,如:

  1. class Promise { 
  2.     then(onFulfilled, onRejected) { 
  3.         onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 如果onFulfilled不是函数,则初始化一个完成处理函数 
  4.             return value; 
  5.         }; 
  6.         onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 如果onRejected不是函数,则初始化一个未完成处理函数 
  7.             throw reason; // 传什么就抛出什么 
  8.         } 
  9.     } 

⑤ then方法其实就是一个注册回调的过程,当调用then的这个Promise对象的状态变为完成状态就可以执行onFulfilled回调函数,当Promise对象的状态变为拒绝状态就可以执行onRejected回调函数了。所以回调函数的执行依赖于调用then的Promise的状态。同时为了支持链式调用,then方法还需要返回一个Promise对象。根据前面的Promise规范,传入的回调函数必须异步执行,这里用setTimeout进行模拟。

  1. class Promise { 
  2.     then(onFulfilled, onRejected) { 
  3.         ...... 
  4.         let promise; 
  5.         switch(this.state) { 
  6.             case FULFILLED: // 调用then方法的时候,当前Promise状态已经变成完成状态,则可用立即执行完成的回调函数 
  7.                 promise = new Promise((resolve, reject) => { 
  8.                     setTimeout(() => { 
  9.                         try { 
  10.                             let x = onFulfilled(this.value); 
  11.                         } catch(e) { 
  12.                             console.log(e); // 打印错误信息 
  13.                             reject(e); 
  14.                         } 
  15.                     }); 
  16.                 }); 
  17.                 break; 
  18.             case REJECTED: 
  19.                 promise = new Promise((resolve, reject) => { 
  20.                     setTimeout(() => { 
  21.                         try { 
  22.                             let x = onRejected(this.reason); 
  23.                         } catch(e) { 
  24.                             reject(e); 
  25.                         } 
  26.                     }); 
  27.                 } 
  28.                 break; 
  29.             case PENDING: 
  30.                 promise = new Promise((resolve, reject) => { 
  31.                     // TODO 
  32.                 }); 
  33.                 break; 
  34.         } 
  35.         return promise; 
  36.     } 

⑥ 当调用then的Promise对象处于pending状态的时候,此时通过then注册的回调函数不能立即执行,必须等待Promise的状态变为最终状态才能执行注册的回调函数。这里就涉及到了一个发布订阅模式。我们可以先将回调函数保存起来,那么什么时候Promise才会变成最终状态呢?那就是调用resolve或reject的时候,所以我们可以在调用resolve或reject的时候,取出注册的回调函数然后执行即可。

  1. class Promise { 
  2.     constructor(executor) { 
  3.         const resolve = (value) => { 
  4.             if (this.state === PENDING) { // 防止用户多次resolve,以第一次resolve为准 
  5.                 ...... 
  6.                 this.onFulfilleds.forEach(fn => fn()); // 取出then中注册的完成回调函数并执行 
  7.             } 
  8.         }; 
  9.         const reject = (reason) => { 
  10.             if (this.state === PENDING) { // 防止用户多次reject 
  11.                 ...... 
  12.                 this.onRejecteds.forEach(fn => fn()); // 取出then中注册的拒绝回调函数并执行 
  13.             } 
  14.         }; 
  15.     } 
  16.     then(onFulfilled, onRejected) { 
  17.         ...... 
  18.         switch(this.state) { 
  19.             case PENDING: 
  20.                 promise = new Promise((resolve, reject) => { 
  21.                     this.onFulfilleds.push(() => { 
  22.                         try { 
  23.                             let x = onFulfilled(this.value); 
  24.                         } catch(e) { 
  25.                             console.log(e); // 打印错误信息 
  26.                             reject(e); 
  27.                         } 
  28.                     }); 
  29.                     this.onRejecteds.push(() => { 
  30.                         try { 
  31.                             let x = onRejected(this.reason); 
  32.                         } catch(e) { 
  33.                             reject(e); 
  34.                         } 
  35.                     }); 
  36.                 }); 
  37.                 break; 
  38.         } 
  39.     } 

⑦ 接下来就是要处理then注册的回调函数的返回值了,因为回调函数的返回值可能是各种各样的情况,可能是普通的值,可能是Promise对象,也可能是带then方法的对象,所以我们要一一进行处理。这里我们使用一个单独的方法resolvePromise()进行各种情况的处理,如:

  1. // 传入then()方法中创建的Promise对象,回调函数的返回值x,then()方法中创建的Promise的resolve、reject 
  2. const resolvePromise = function(promise, x, resolve, reject) { 
  3.     // TODO 
  4. class Promise { 
  5.     constructor(executor) { // 传入执行器函数 
  6.         ...... 
  7.     } 
  8.     then(onFulfilled, onRejected) { 
  9.         case FULFILLED: 
  10.             promise = new Promise((resolve, reject) => { 
  11.                 ...... 
  12.                 let x = onFulfilled(this.value); 
  13.                 resolvePromise(promise, x, resolve, reject); // 处理回调函数的返回值 
  14.             }); 
  15.         case REJECTED: 
  16.             promise = new Promise((resolve, reject) => { 
  17.                 ...... 
  18.                 let x = onRejected(this.reason); 
  19.                 resolvePromise(promise, x, resolve, reject); // 处理回调函数的返回值 
  20.             }); 
  21.         case PENDING: 
  22.             this.onFulfilleds.push(() => { 
  23.                 let x = onFulfilled(this.value); 
  24.                 resolvePromise(promise, x, resolve, reject); // 处理回调函数的返回值 
  25.             }); 
  26.             this.onRejecteds.push(() => { 
  27.                 let x = onRejected(this.reason); 
  28.                 resolvePromise(promise, x, resolve, reject); // 处理回调函数的返回值 
  29.             }); 
  30.     } 

三、实现resolvePromise
① 如果回调函数返回值与then()方法中创建的Promise对象相同则抛出错误,这相当于是自己等自己会进入死循环。

  1. let p1 = new Promise((resolve, reject) => { 
  2.     resolve(1); 
  3. }) 
  4. let p2 = p1.then((value) => { // p2即then方法内创建Promise对象 
  5.     return p2; 
  6. }); 
  7. // 结果抛出错误,显示Chaining cycle detected for promise # 

  1. const resolvePromise = function(promise, x, resolve, reject) { 
  2.     if (promise === x) { // 禁止resolve自己 
  3.         throw new Error("Chaining cycle detected for promise #"); 
  4.     } 

② 如果回调函数返回的是一个Promise对象或者带then方法的类Promise对象,又或者一个函数,因为函数上也可能有then方法,那么我们需要取出then方法并执行,对于Promise对象而言,then方法的执行就会注册相应的回调函数,等Promise状态变为最终状态后就会执行对应的回调函数,回调函数执行后就可以拿到Promise对象的value值,然后将该value值作为调用then方法创建的Promise的对象的value值。

  1. const resolvePromise = function(promise, x, resolve, reject) { 
  2.     ...... 
  3.     if ((x && typeof x === "object") || typeof x === "function") { // 如果是对象或者函数,函数也可能有then方法 
  4.         let executed; 
  5.         try { 
  6.             let then = x.then; // 尝试取出then方法 
  7.             if (typeof then === "function") { // 如果该对象上存在then方法,那么是个Promise对象或者包含then方法的对象 
  8.                 then.call(x, function (y) { // 执行then方法,对于真正的Promise对象,则会注册回调,等到状态变化后,回调函数会执行,回调中能接收到Promise的value值 
  9.                     if (executed) return
  10.                     executed = true; // 注册的回调函数只能执行一次 
  11.                     resolvePromise(promise, y, resolve, reject); // 返回值还可能是一个Promise对象,故需要递归直到变为普通值为止 
  12.                 }, function (e) { 
  13.                     if (executed) return
  14.                     executed = true
  15.                     reject(e); 
  16.                 }); 
  17.             } else { // 不包含then方法的普通对象,直接resolve即可 
  18.                 resolve(x);       
  19.             } 
  20.         } catch(e) { 
  21.             if (executed) return
  22.             executed = true
  23.             reject(e); 
  24.         } 
  25.     } else { 
  26.         resolve(x); 
  27.     } 

四、实现catch
catch可以看做是一个特殊的then方法,其内部会调用then()方法,但是仅注册拒绝的回调函数,这也就是then(onFulfilled, onRejected)和then(onFulfilled).catch(onRejected)的区别,如果将onRejected写到then中,那么当then的onFulfilled发生错误的时候,onRejected就无法捕获到其中的错误,而写到catch中,那么就相当于是下一个then()方法,故能捕获到上一个then()方法中发生的错误。

  1. class Promise { 
  2.     catch(onRejected) { 
  3.         return this.then(null, onRejected); // 仅注册拒绝的回调函数 
  4.     } 

五、总结
Promise其实就是一个类,内部有state、value、reason等属性,分别用于存储当前Promise的状态、执行成功后的返回值,执行失败的原因,同时内部还提供了resolve、reject两个方法,这两个方法会以参数的形式传递给执行器,即传递到外部,以便修改Promise的状态。

Promise还提供了一个then方法用于注册回调函数,注册回调的时候与当前Promise的状态有关,如果是最终状态,则立即执行,如果是等待状态,则先保存起来,等到调用resolve或reject方法的时候再取出回调并执行。注册的回调函数可能会返回各种各样的值:

如果返回的是普通值,那么直接用then返回的Promise的resolve方法resolve即可;

如果返回的是Promise对象或者是带then方法的对象或函数,那么需要调用其then方法并注册一个自定义回调用于接收当前Promise的值,等该Promise变为最终状态后会执行回调就可以拿到其value,最后将其作为then返回的Promise的value,即resolve(x)。完整源码如下:

  1. var PENDING = "pending"; // 等待状态 
  2. var FULFILLED = "fulfilled"; // 执行、完成状态 
  3. var REJECTED = "rejected"; // 拒绝、未完成状态 
  4. // 传入then()方法中创建的Promise对象,回调函数的返回值x,then()方法中创建的Promise的resolve、reject 
  5. const resolvePromise = function(promise, x, resolve, reject) { 
  6.     if (promise === x) { // 禁止resolve自己 
  7.         throw new Error("Chaining cycle detected for promise #"); 
  8.     } 
  9.     if ((x && typeof x === "object") || typeof x === "function") { // 如果是对象或者函数,函数也可能有then方法 
  10.         let executed; 
  11.         try { 
  12.             let then = x.then; // 尝试取出then方法 
  13.             if (typeof then === "function") { // 如果该对象上存在then方法,那么是个Promise对象或者包含then方法的对象 
  14.                 then.call(x, function (y) { // 执行then方法,对于真正的Promise对象,则会注册回调,等到状态变化后,回调函数会执行,回调中能接收到Promise的value值 
  15.                     if (executed) return
  16.                     executed = true; // 注册的回调函数只能执行一次 
  17.                     resolvePromise(promise, y, resolve, reject); // 返回值还可能是一个Promise对象,故需要递归直到变为普通值为止 
  18.                 }, function (e) { 
  19.                     if (executed) return
  20.                     executed = true
  21.                     reject(e); 
  22.                 }); 
  23.             } else { // 不包含then方法的普通对象,直接resolve即可 
  24.                 resolve(x); 
  25.             } 
  26.         } catch(e) { 
  27.             if (executed) return
  28.             executed = true
  29.             reject(e); 
  30.         } 
  31.     } else { 
  32.         resolve(x); 
  33.     } 
  34. class Promise { 
  35.     constructor(executor) { // 传入执行器函数 
  36.         this.state = PENDING; // Promise对象创建完成后默认为等待状态 
  37.         this.value = undefined; // 保存异步操作的结果 
  38.         this.reason = undefined; // 保存失败的原因 
  39.         this.onFulfilleds = []; // 保存then中注册的完成回调函数 
  40.         this.onRejecteds = []; // 保存then中注册的拒绝回调函数 
  41.         const resolve = (value) => { 
  42.             if (this.state === PENDING) { // 防止用户多次resolve,以第一次resolve为准 
  43.                 this.value = value; 
  44.                 this.state = FULFILLED; // 将Promise对象的状态改为完成状态 
  45.                 this.onFulfilleds.forEach(fn => fn()); // 取出then中注册的完成回调函数并执行 
  46.             } 
  47.         }; 
  48.         const reject = (reason) => { 
  49.             if (this.state === PENDING) { // 防止用户多次reject 
  50.                 this.reason = reason; 
  51.                 this.state = REJECTED; // 将Promise对象的状态改为未完成状态 
  52.                 this.onRejecteds.forEach(fn => fn()); // 取出then中注册的拒绝回调函数并执行 
  53.             } 
  54.         }; 
  55.         try { 
  56.             executor(resolve, reject); // 执行器由用户传入可能会发生错误,所以需要进行捕获 
  57.         } catch(e) { 
  58.             reject(e); 
  59.         } 
  60.     } 
  61.     then(onFulfilled, onRejected) { 
  62.         onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => { // 如果onFulfilled不是函数,则初始化一个完成处理函数 
  63.             return value; 
  64.         }; 
  65.         onRejected = typeof onRejected === "function" ? onRejected : (reason) => { // 如果onRejected不是函数,则初始化一个未完成处理函数 
  66.             throw reason; // 传什么就抛出什么 
  67.         } 
  68.         let promise; 
  69.         switch(this.state) { 
  70.             case FULFILLED: // 调用then方法的时候,当前Promise状态已经变成完成状态,则可用立即执行完成的回调函数 
  71.                 promise = new Promise((resolve, reject) => { 
  72.                     setTimeout(() => { 
  73.                         try { 
  74.                             let x = onFulfilled(this.value); 
  75.                             resolvePromise(promise, x, resolve, reject); 
  76.                         } catch(e) { 
  77.                             console.log(e); 
  78.                             reject(e); 
  79.                         } 
  80.                     }); 
  81.                 }); 
  82.                 break; 
  83.              case REJECTED: 
  84.                 promise = new Promise((resolve, reject) => { 
  85.                     setTimeout(() => { 
  86.                         try { 
  87.                             let x = onRejected(this.reason); 
  88.                             resolvePromise(promise, x, resolve, reject); 
  89.                         } catch(e) { 
  90.                             reject(e); 
  91.                         } 
  92.                     }); 
  93.                 }); 
  94.                 break; 
  95.             case PENDING: 
  96.                 promise = new Promise((resolve, reject) => { 
  97.                     this.onFulfilleds.push(() => { 
  98.                         try { 
  99.                             let x = onFulfilled(this.value); 
  100.                             resolvePromise(promise, x, resolve, reject); 
  101.                         } catch(e) { 
  102.                             reject(e); 
  103.                         } 
  104.                     }); 
  105.                     this.onRejecteds.push(() => { 
  106.                         try { 
  107.                             let x = onRejected(this.reason); 
  108.                             resolvePromise(promise, x, resolve, reject); 
  109.                         } catch(e) { 
  110.                             reject(e); 
  111.                         } 
  112.                     }); 
  113.                 }); 
  114.                 break; 
  115.             } 
  116.         return promise; 
  117.     } 
  118.     catch(onRejected) { 
  119.         return this.then(null, onRejected); // 仅注册拒绝的回调函数 
  120.     } 

 

 

 

 

来源:晨曦大前端内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯