从 TS 造 Promise 的过程建立前端安全感

你还在用 npm 的 star 数来选择依赖吗?在 npm 安全性问题随时爆发 的今天,作为前端开发者的我们应该具备源码阅读的能力,最好知道自己在用什么,这样在使用外部 npm 依赖时才有安全感不是么?

最近遇到一个细思极恐的问题,笔者最近老是收到防脱广告推送亦或是一些与笔者最近说出来的话相关的广告,以前只知道网上的信息流会被窃据,如今难不成语音也会监听窃取了???吓得我赶紧关掉了所有麦的权限。虽然我们不能自己造个手机,但也不能活得没有安全感。

拿 axios 这个我们常用的依赖来说,如果哪天被篡改了,那后果真不敢想象。即便这基本是不可能的,但切图仔中的精英不能就此停止追求进步的脚步。好在笔者前些天写了篇 axios 重构经验分享,多少可以证明自己努力过。 ?

这还不够,翻了下最常用的依赖,其中 es6-promise 特别惹眼 (源码比较晦涩难懂,说白了就是有点乱),本来早就想深入了解 Promise ,于是毫不犹豫决定造它。由于笔者在过渡到 TypeScript ,所以本次开发依旧会采用 TypeScript 来敲。

这应该是笔者最后一次用 TypeScript 冠名分享文章,再见 ?,我已经可以安全上路了。( 喊了那么多次,快上车,都没有多少人上车,那我就先走了。)

本文适合从零了解或者想重新深入研究 Promise 的读者,并且可以得到如下知识:

  • Promise 重要知识点
  • Promise API 实现方法
  • 前端何来安全感

笔者希望读者可以仅通过看仅此一篇文章就可以对 Promise 有个深刻的认知,并且可以自己实现一个 Promise 类。所以会从方方面面讲 Promise,内容可能会比较多,建议读者选读。

为什么要实现 Promise

Promise 出现在 Es6 ,如果 Es5 需要使用 Promise,通常需要用到 Promise-polyfill 。也就是说,我们要实现的是一个 polyfill。实现它不仅有助于我们深入了解 Promise 而且能减少使用中犯错的概率,以至于获得 Promise 最佳实践。

从另外一个角度来说重新实现某个依赖也是一种源码解读的方式,坚持这么做,意味着解读源码能力的提升。

Promise

Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

来看笔者用心画的一张 API 结构图 ( 看不清楚的可以进我的 GitHub 看,有大图和 xmind 源文件 ):

上图只是一个 Promise 的 API 蓝图,其实 Promises/A+ 规范并不设计如何创建、解决和拒绝 promise,而是专注于提供一个通用的 then 方法。所以,Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。如果大家都按规范来,那么就没有那么多兼容问题。(PS:包括 web 标准 ) 接着聊下 Promises/A+,看过的可以跳过。

Promises/A+

所有 Promise 的实现都离不开 Promises/A+ 规范,内容不多,建议大家可以过一遍。这边讲一些规范中重要的点

术语

  • Promise 一个拥有 then 方法的对象或函数,其行为符合 Promises/A+ 规范;
  • thenable 一个定义了 then 方法的对象或函数,也可视作 “拥有 then 方法”
  • 值(value)任何 JavaScript 的合法值(包括 undefined , thenable 和 promise)
  • 异常(exception) 使用 throw 语句抛出的一个值
  • 据因(reason) 表示一个 promise 的拒绝原因。

Promise 的状态

一个 Promise 的当前状态 必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。
  • 等待态(Pending)

    处于等待态时,promise 需满足:可以迁移至执行态或拒绝态

  • 执行态(Fulfilled)

    处于执行态时,promise 需满足:不能迁移至其他任何状态,必须拥有一个不可变终值

  • 拒绝态(Rejected)

    处于拒绝态时,promise 需满足:不能迁移至其他任何状态,必须拥有一个不可变据因

这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变( 指当 value 或 reason 不是 基本值时,只要求其引用地址相等,但属性值可被更改)。

Then 方法

一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。

promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected);
  • onFulfilledonRejected 都是可选参数。
  • 如果 onFulfilled 是函数,当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值,在 promise 执行结束前其不可被调用,其调用次数不可超过一次
  • 如果 onRejected 是函数,当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因,在 promise 被拒绝执行前其不可被调用,其调用次数不可超过一次
  • onFulfilledonRejected 只有在执行环境堆栈仅包含平台代码 ( 指的是引擎、环境以及 promise 的实施代码 )时才可被调用
  • 实践中要确保 onFulfilledonRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
  • onFulfilledonRejected 必须被作为函数调用即没有 this 值 ( 也就是说在 严格模式(strict) 中,函数 this 的值为 undefined ;在非严格模式中其为全局对象。)
  • then 方法可以被同一个 promise 调用多次
  • then 方法必须返回一个 promise 对象

Then 参数 (函数) 返回值

希望读者可以认真看这部分的内容,对于理解 promise 的 then 方法有很大的帮助。

先来看下 promise 执行过程:

大致的过程是,promise 会从 pending 转为 fulfilledrejected ,然后对应调用 then 方法参数的 onFulfilledonRejected ,最终返回 promise 对象。

进一步理解,假定 有如下两个 promise:

promise2 = promise1.then(onFulfilled, onRejected);

会有以下几种情况:

  1. 如果 onFulfilled 或者 onRejected 抛出异常 e ,则 promise2 必须拒绝执行,并返回 拒因 e
  2. 如果 onFulfilled 不是函数 且 promise1 成功执行, promise2 必须成功执行并返回 相同的值
  3. 如果 onRejected 不是函数 且 promise1 拒绝执行, promise2 必须拒绝执行并返回 相同的据因

希望进一步搞懂的,可以将下面代码拷贝到 chrome 控制台或其他可执行环境感受一下:

// 通过改变 isResolve 来切换 promise1 的状态
const isResolve = true;

const promise1 = new Promise((resolve, reject) => {
  if (isResolve) {
    resolv
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值