作者:Morrain

转发链接:https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ

前言

Promise 是异步编程的一种解决方案,它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。更多关于 Promise 的介绍请参考阮一峰老师的 ES6入门 之 Promise 对象。

很多同学在学习 Promise 时,知其然却不知其所以然,对其中的用法理解不了。本系列文章由浅入深逐步实现 Promise,并结合流程图、实例以及动画进行演示,达到深刻理解 Promise 用法的目的。

本系列文章由如下几个章节组成:

图解 Promise 实现原理(一):基础实现

图解 Promise 实现原理(二):Promise 链式调用

图解 Promise 实现原理(三):Promise 原型方法实现

图解 Promise 实现原理(四):Promise 静态方法实现(本篇)

上一节中,实现了 Promise 的原型方法。包括增加异常状态,catch以及 finally。截至目前,Promise 的实现如下:

class Promise {  callbacks = [];  state = 'pending';//增加状态  value = null;//保存结果  constructor(fn) {    fn(this._resolve.bind(this), this._reject.bind(this));  }  then(onFulfilled, onRejected) {    return new Promise((resolve, reject) => {      this._handle({        onFulfilled: onFulfilled || null,        onRejected: onRejected || null,        resolve: resolve,        reject: reject      });    });  }  catch(onError) {    return this.then(null, onError);  }  finally(onDone) {    if (typeof onDone !== 'function') return this.then();     let Promise = this.constructor;    return this.then(      value => Promise.resolve(onDone()).then(() => value),      reason => Promise.resolve(onDone()).then(() => { throw reason })    );  }  _handle(callback) {    if (this.state === 'pending') {      this.callbacks.push(callback);      return;    }     let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;     if (!cb) {//如果then中没有传递任何东西      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;      cb(this.value);      return;    }     let ret;     try {      ret = cb(this.value);      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;    } catch (error) {      ret = error;      cb = callback.reject    } finally {      cb(ret);    }   }  _resolve(value) {     if (value && (typeof value === 'object' || typeof value === 'function')) {      var then = value.then;      if (typeof then === 'function') {        then.call(value, this._resolve.bind(this), this._reject.bind(this));        return;      }    }     this.state = 'fulfilled';//改变状态    this.value = value;//保存结果    this.callbacks.forEach(callback => this._handle(callback));  }  _reject(error) {    this.state = 'rejected';    this.value = error;    this.callbacks.forEach(callback => this._handle(callback));  }}

(滑动可查看)

接下来再介绍一下 Promise 种静态方法的实现,譬如 Promise.resolve、Promise.reject、Promise.all 和 Promise.race。其它静态方法的实现也是类似的。

【静态方法】

1、Promise.resolve && Promise.reject

除了前文中提到的 Promise实例的原型方法外,Promise 还提供了 Promise.resolve 和Promise.reject 方法。用于将非 Promise 实例包装为 Promise 实例。例如:

Promise.resolve('foo')// 等价于new Promise(resolve => resolve('foo'))

(滑动可查看)

Promise.resolve 的参数不同对应的处理也不同,如果 Promise.resolve 的参数是一个 Promise的实例,那么 Promise.resolve 将不做任何改动,直接返回这个 Promise 实例,如果是一个基本数据类型,譬如上例中的字符串,Promise.resolve 就会新建一个 Promise 实例返回。这样当我们不清楚拿到的对象到底是不是 Promise 实例时,为了保证统一的行为,Promise.resolve 就变得很有用了。看一个例子:

const Id2NameMap = {};const getNameById = function (id) {   if (Id2NameMap[id]) return Id2NameMap[id];   return new Promise(resolve => {    mockGetNameById(id, function (name) {      Id2NameMap[id] = name;      resolve(name);    })  });}getNameById(id).then(name => {  console.log(name);});

(滑动可查看)

上面的场景我们会经常碰到,为了减少请求,经常会缓存数据,我们获取到 id 对应的名字后,存到 Id2NameMap 对象里,下次再通过 id 去请求 id 对应的 name 时先看 Id2NameMap里有没有,如果有就直接返回对应的 name,如果没有就发起异步请求,获取到后放到 Id2NameMap 中去。

其实上面的代码是有问题的,如果命中 Id2NameMap 里的值,getNameById 返回的结果就是 name,而不是 Promise 实例。此时 getNameById(id).then 会报错。在我们不清楚返回的是否是 Promise 实例的情况下,就可以使用 Promise.resolve 进行包装:

Promise.resolve(getNameById(id)).then(name => {  console.log(name);});

(滑动可查看)

这样一来,不管 getNameById(id) 返回的是什么,逻辑都没有问题。看下面的Demo:

demo-Promise.resolve 的源码

地址:https://repl.it/@morrain2016/demo-Promiseresolve

在实现 Promise.resolve 之前,我们先看下它的参数分为哪些情况:

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。

(2)参数是一个 thenable 对象

thenable 对象指的是具有 then 方法的对象,比如下面这个对象。

let thenable = {  then: function(onFulfilled) {    onFulfilled(42);  }};

(滑动可查看)

Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then方法。

let thenable = {  then: function(onFulfilled) {    onFulfilled(42);  }}; let p1 = Promise.resolve(thenable);p1.then(function(value) {  console.log(value);  // 42});

(滑动可查看)

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

(3)参数不是具有 then 方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。

(4)不带任务参数

Promise.resolve 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。

static resolve(value) {  if (value && value instanceof Promise) {    return value;  } else if (value && typeof value === 'object' && typeof value.then === 'function') {    let then = value.then;    return new Promise(resolve => {      then(resolve);    });    } else if (value) {    return new Promise(resolve => resolve(value));  } else {    return new Promise(resolve => resolve());  }}

(滑动可查看)

Promise.reject 与 Promise.resolve 类似,区别在于 Promise.reject 始终返回一个状态的 rejected 的 Promise 实例,而 Promise.resolve 的参数如果是一个 Promise 实例的话,返回的是参数对应的 Promise 实例,所以状态不一定。

Promise.reject 的实现源码

地址:https://repl.it/@morrain2016/Promise

2、Promise.all && Promise.race

Promise.all 接收一个 Promise 实例的数组,在所有这些 Promise 的实例都 fulfilled 后,按照 Promise 实例的顺序返回相应结果的数组。

const p1 = new Promise((resolve, reject) => {  setTimeout(() => resolve('p1'), 1000)})  const p2 = new Promise((resolve, reject) => {  setTimeout(() => resolve('p2'), 5000)}) Promise.all([p1, p2]).then(rets => {   console.log(rets) // ['p1','p2']})

(滑动可查看)

Promise.all 的实现如下:

static all(promises) {  return new Promise((resolve, reject) => {    let fulfilledCount = 0    const itemNum = promises.length    const rets = Array.from({ length: itemNum })    promises.forEach((promise, index) => {      Promise.resolve(promise).then(result => {        fulfilledCount++;        rets[index] = result;        if (fulfilledCount === itemNum) {          resolve(rets);        }      }, reason => reject(reason));    })  })}

(滑动可查看)

Promise.all 的实现源码

地址:https://repl.it/@morrain2016/Promise

Promise.race 也接收一个 Promise 实例的数组,与 Promise.all不同的是,所以返回的结果是这些 Promise 实例中最先 fulfilled 的。

const p1 = new Promise((resolve, reject) => {  setTimeout(() => resolve('p1'), 1000)})  const p2 = new Promise((resolve, reject) => {  setTimeout(() => resolve('p2'), 5000)}) Promise.race([p1, p2]).then(ret => {   console.log(ret) // 'p1'})

(滑动可查看)

Promise.race 的实现如下:

static race(promises) {  return new Promise(function (resolve, reject) {    for (let i = 0; i < promises.length; i++) {      Promise.resolve(promises[i]).then(function (value) {        return resolve(value)      }, function (reason) {        return reject(reason)      })    }  })}

(滑动可查看)

Promise.race 的实现源码

地址:https://repl.it/@morrain2016/Promise

【总结】

刚开始看 Promise 源码的时候总不能很好的理解 then 和 resolve 函数的运行机理,但是如果你静下心来,反过来根据执行 Promise 时的逻辑来推演,就不难理解了。这里一定要注意的点是:Promise 里面的 then 函数仅仅是注册了后续需要执行的代码,真正的执行是在 resolve 方法里面执行的,理清了这层,再来分析源码会省力的多。

现在回顾下 Promise 的实现过程,其主要使用了设计模式中的观察者模式:

  1. 通过 Promise.prototype.then 和 Promise.prototype.catch 方法将观察者方法注册到被观察者 Promise 对象中,同时返回一个新的 Promise 对象,以便可以链式调用。
  2. 被观察者管理内部 pending、fulfilled 和 rejected 的状态转变,同时通过构造函数中传递的 resolve 和 reject 方法以主动触发状态转变和通知观察者。

本系列图文讲解的是 Promise 的思想,实现的内容并不能完全满足 Promise/A+ 规范的所有要求。

推荐JavaScript经典实例学习资料文章

《图解 Promise 实现原理(一):基础实现》

《图解 Promise 实现原理(二):Promise 链式调用》

《图解 Promise 实现原理(三):Promise 原型方法实现》

《实践教你从零构建前端 Lint 工作流「干货」》

《高性能多级多选级联组件开发「JS篇」》

《深入浅出讲解Node.js CLI 工具最佳实战》

《延迟加载图像以提高Web网站性能的五种方法「实践」》

《比较 JavaScript 对象的四种方式「实践」》

《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》

《前端如何一次性处理10万条数据「进阶篇」》

《推荐三款正则可视化工具「JS篇」》

《如何让用户选择是否离开当前页面?「JS篇」》

《JavaScript开发人员更喜欢Deno的五大原因》

《仅用18行JavaScript实现一个倒数计时器》

《图文细说JavaScript 的运行机制》

《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》

《推荐Web程序员常用的15个源代码编辑器》

《10个实用的JS技巧「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》

《深入JavaScript教你内存泄漏如何防范》

《手把手教你7个有趣的JavaScript 项目-上「附源码」》

《手把手教你7个有趣的JavaScript 项目-下「附源码」》

《JavaScript 使用 mediaDevices API 访问摄像头自拍》

《手把手教你前端代码如何做错误上报「JS篇」》

《一文让你彻底搞懂移动前端和Web 前端区别在哪里》

《63个JavaScript 正则大礼包「值得收藏」》

《提高你的 JavaScript 技能10 个问答题》

《JavaScript图表库的5个首选》

《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》

《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》

《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》

《可视化的 js:动态图演示 Promises & Async/Await 的过程》

《原生JS封装拖动验证滑块你会吗?「实践」》

《如何实现高性能的在线 PDF 预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug 3.0.0正式发布,不再支持 Node.js 6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript 20 年 中文版之创立标准》

《值得收藏的前端常用60余种工具方法「JS篇」》

《箭头函数和常规函数之间的 5 个区别》

《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》

《「前端篇」不再为正则烦恼》

《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》

《深入细品浏览器原理「流程图」》

《JavaScript 已进入第三个时代,未来将何去何从?》

《前端上传前预览文件 image、text、json、video、audio「实践」》

《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧「值得收藏」》

《前端必备基础知识:window.location 详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段「值得收藏」》

《Node + H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation 在 JS 中的应用「前端篇」》

作者:Morrain

转发链接:https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ

promise用法_图解 Promise 实现原理(四):Promise 静态方法实现相关推荐

  1. promise用法_图解 Promise 实现原理(一)—— 基础实现

    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ 作者:孔垂亮 很多同学在学习 Promise 时 ...

  2. promise用法_【JavaScript 教程】异步操作——Promise 对象

    作者 | 阮一峰 概述 Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口.它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操 ...

  3. 手写一个promise用法_手写一个自己的 JavaScript Promise 类库

    终于有时间写这篇文章了, ES2015 推出了JS 的 Promise ,而在没有原生支持的时候,我们也可以使用诸如 Promises/A+ 的库的帮助,在我们的代码里实现Promise 的支持: 如 ...

  4. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  5. 手写一个promise用法_手写一个Promise

    JS面向对象 在JS中一切皆对象,但JS并不是一种真正的面向对象(OOP)的语言,因为它缺少类(class)的概念.虽然ES6引入了class和extends,使我们能够轻易地实现类和继承.但JS并不 ...

  6. mysql新增列并同时增加数据_图解MySQL | 「原理解析」 MySQL 为表添加列 是怎么立刻完成的...

    原创作者:图解MySQL 在上一期图解 图解MySQL | MySQL DDL为什么成本高?中,我们介绍了: 传统情况下,为表添加列需要对表进行重建 腾讯团队为 MySQL 引入了 Instant A ...

  7. 红黑树与平衡二叉树_图解“红黑树”原理,一看就明白!

    " 学过数据结构都知道二叉树的概念,而又有多种比较常见的二叉树类型,比如完全二叉树.满二叉树.二叉搜索树.均衡二叉树.完美二叉树等. 图片来自 Pexels 今天我们要说的红黑树就是就是一棵 ...

  8. yarn的组成部分_图解YARN工作原理

    YARN 即MapReduce V2版本.相比MapReduce V1 它有很多优点: 1. 分散了jobTracker 的任务.资源管理任务由资源管理器负责,作业启动.运行和监测任务由分布在集群节点 ...

  9. mysql数据丢失_图解MySQL | 「原理解析」 MySQL使用固定的server_id导致数据丢失

    原创作者:爱可生开源社区 本文我们来看一个场景,两台MySQL实例使用主从复制,当master故障,触发高可用切换,新master上线后,通过备份重建旧master并建立复制后,数据发生丢失. 以下我 ...

最新文章

  1. 编译原理lr(0)c语言,关于编译原理:LR(1)LR(0)文法判断?
  2. SAP gateway 后台系统的 OData service 服务探测机制实现原理
  3. The hierarchy of the type UserOperateLogAdvisor is inconsistent
  4. python 测试框架
  5. 四大金刚 数据结构_GIS技术在气象领域应用综述
  6. Spark入门(十七)之单表关联
  7. java 线程安全的单例_线程安全的单例模式的几种实现
  8. 前端学习(2538):slice
  9. discuz MVC结构分析
  10. 使用docker搭建wordpress网站
  11. python-matplotlib学习(1)
  12. MySql 数据库安装、环境变量配置 以及 本地连接
  13. c语言屏蔽一段程序,C语言#if 0阻止代码段
  14. 炜煌E30 E31微型热敏打印机 STM32 串口驱动
  15. 当滑雪这项世界最古老的运动遇上AI
  16. 如何计算CRC校验码(循环冗余检验码)
  17. cgroups的实现
  18. 关于EOSUNION 的商业创业大赛的一点心得
  19. 魔众文库系统 v2.2.0 常规升级,界面和后台优化
  20. Html代码写得个人简历

热门文章

  1. 广播电视加速技术迭代,如何用新技术拥抱行业转型?
  2. 菜鸟网络宣布推出物流加速上云行动“鲲鹏计划”
  3. 哪种人是软件设计中的稀缺型人才?
  4. 彻底弄懂为什么不能把栈上分配的数组(字符串)作为返回值
  5. 阿里开发者们的第13个感悟:工程师需要在循环迭代中成长
  6. 重磅发布!阿里云推PostgreSQL 10 高可用版
  7. 今天来聊聊 Redis 的主从复制
  8. 与变异风险词赛跑 阿里探索AI治理网络风险
  9. 漫画:什么是自动驾驶?
  10. 想学 Python?那这套教程再适合你不过了!!