type
status
date
slug
summary
tags
category
icon
password
😀
Promise早在ES2015中就新增了此特性,并且广为流行,而如今已经是ES2016的时代,不妨碍我们进一步学习和了解Promise
 
 

Promise基础

用一句话解释Promise?Promise可以代指那些尚未完成的一些操作,但是其在未来的某个时间会返回某一特定的结果。
当创建一个Promise实例后,其代表一个未知的值,在将来的某个时间会返回一个成功的返回值,或者失败的返回值,我们可以为这些返回值添加处理函数,当值返回时,处理函数被调用。Promise总是处于下面三种状态之一:
  • pending: Promise的初始状态,也就是未被fulfilled或者rejected的状态。
  • fulfilled: 意味着promise代指的操作已经成功完成。
  • rejected:意味着promise代指的操作由于某些原因失败。
一个处于pending状态的promise可能由于某个成功返回值而发展为fulfilled状态,也有可能因为某些错误而进入rejected状态,无论是进入fulfilled状态或者rejected状态,绑定到这两种状态上面的处理函数就会被执行。并且进入fulfilled或者rejected状态也就不能再返回pending状态了。

一步步实现Promise

接下来我们就开始实现自己的Promise对象

第一步:Promise构造函数

Promise有三种状态,pending、fulfilled、rejected。
有了三种状态后,那么我们怎么创建一个Promise实例呢?
const promise = new Promise(executor) // 创建Promise的语法
通过上面生成promise语法我们知道,Promise实例是调用Promise构造函数通过new操作符生成的。这个构造函数我们可以先这样写:
我们可以看到上面构造函数接受的参数executor。它是一个函数,并且接受其他两个函数(resolve和reject)作为参数,当resolve函数调用后,promise的状态转化为fulfilled,并且执行成功返回的处理函数(不用着急后面会说到怎么添加处理函数)。当reject函数调用后,promise状态转化为rejected,并且执行失败返回的处理函数。
现在我们的代码大概是这样的:
Dont Repeat Yourselt!!!我们可以看到上面代码后面两个函数基本相同,其实我们可以把它整合成一个函数,在结合高阶函数的使用。
现在我们的代码就看上去简洁多了。

第二步:为Promise添加处理函数

其实通过 new Promise(executor)已经可以生成一个Promise实例了,甚至我们可以通过传递到executor中的resolve和reject方法来改变promise状态,但是!现在的promise依然没啥卵用!!!因为我们并没有给它添加成功和失败返回的处理函数。
首先我们需要给我们的promise增加两个属性,successListener和failureListener用来分别缓存成功处理函数和失败处理函数。
怎么添加处理函数呢?ECMASCRIPT标准中说到,我们可以通过promise原型上面的then方法为promise添加成功处理函数和失败处理函数,可以通过catch方法为promise添加失败处理函数。
我们现在的Promise基本初具雏形了。甚至可以运用到一些简单的场景中了。举个例子。

第三步:Promise的链式调用

Promise需要实现链式调用,我们需要再次回顾下then方法的定义:
then方法为pormise添加成功和失败的处理函数,同时then方法返回一个新的promise对象,这个新的promise对象resolve处理函数的返回值,或者当没有提供处理函数时直接resolve原始的值。
可以看出,promise能够链式调用归功于then方法返回一个全新的promise,并且resolve处理函数的返回值,当然,如果then方法的处理函数本身就返回一个promise,那么久不用我们自己手动生成一个promise了。了解了这些,就开始动手写代码了。
首先我们写了一个isPromise方法,用于判断一个对象是否是promise。就是判断对象是否有一个then方法,免责声明为了实现上的简单,我们不区分thenable和promise的区别,但是我们应该是知道。所有的promise都是thenable的,而并不是所有的thenable对象都是promise。(thenable对象是指带有一个then方法的对象,该then方法其实就是一个executor。)isPromise的作用就是用于判断then方法返回值是否是一个promise,如果是promise,就直接返回该promise,如果不是,就新生成一个promise并返回该promise。
由于需要链式调用,我们对successListener和failureListener中处理函数进行了重写,并不是直接push进去then方法接受的参数函数了,因为then方法需要返回一个promise,所以当then方法里面的处理函数被执行的同时,我们也需要对then方法返回的这个promise进行处理,要么resolve,要么reject掉。当然,大部分情况都是需要resolve掉的,只有当then方法没有添加第二个参数函数,同时调用then方法的promise就是rejected的时候,才需要把then方法返回的pormise进行reject处理,也就是调用statusProvider(child, REJECTED)(data).
toy Promise实现的完整代码:

四、怎么让我们的toy Promise变强健

  1. 在ECMAScript标准中,Promise构造函数上面还提供了一些静态方法,比如Promise.resolvePromise.rejectPromsie.allPromise.race。当我们有了上面的基础实现后,为我们的toy Promise添加上面这些新的功能一定能让其更加实用。
  1. 在我们的基本实现中,我们并没有区分thenable对象,其实Promise.resolvethen方法都可以接受一个thenable对象,并把该thenable对象转化为一个promise对象,如果想让我们的toy Promise用于生产的话,这也是要考虑的。
  1. 为了让我们的toy Promise变得更强壮,我们需要拥有强健的错误处理机制,比如验证executor必须是一个函数、then方法的参数只能是函数或者undefined或null,又比如executor和then方法中抛出的错误并不能够被window.onerror监测到,而只能够通过错误处理函数来处理,这也是需要考虑的因素。
  1. 如果我们的Promise polyfill是考虑支持多平台,那么首要考虑的就是浏览器环境或Node.js环境,其实在这两个平台,原生Promise都是支持两个事件的。就拿浏览器端举例:
      • unhandledrejection: 在一个事件循环中,如果我们没有对promise返回的错误进行处理,那么就会在window对象上面触发该事件。
      • rejectionhandled:如果在一个事件循环后,我们才去对promise返回的错误进行处理,那么就会在window对象上面监听到此事件。

📎 参考文章

  • 一些引用
  • 引用文章
 
💡
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~
搞懂防抖、节流及应用场景闭包为什么会占用内存
  • Twikoo