[JavaScript]一起来实现一个Promise

504 阅读6分钟
本文仅作为交流学习,主要为juejiner我的总结复习之用,还望大佬们轻喷

前言

初学js踩过最多的坑是什么,有人说是隐式转换,有人说是this改变,而对我而言,是异步函数。有很多次,被卡在空数据报错里,于是学会了默认值赋值,学会了深层判空,学会了通过异步回调来同步和异步加载数据或者控制状态的加载。

而这里,不可避免的就要学习es6所推崇的promise异步编程方案。上一篇文章《[JavaScript] Promise 与 Ajax/Axios》中我简单介绍了promise的用法。那么这里我们来深延一下promise,看看这方神圣到底能玩出什么❀来~

一 Promise

开始研究之前除了看看阮神的es6,红皮书,和掘友的文章之外,当然得看看比较官方的实现以及相关规范了

promise-polyfill

PromiseA+

what

一个构造函数,其参数为一个excutor,其构造出的对象拥有私有成员state,value,reactios...etc

下面是一个简单的Promise,不用细看扫一眼往下走接着看介绍

// 格式挂了...
function Promise (excutor) {
 let _ = this
 _.state = 'pending'
 _.value = undefined
 _.fullfillArr = []
 _.rejectedArr = []
 let resolve = result => {
   if (this.state !== 'pending') return
   setTimeout(() => {
     this.state = 'fullfilled'
     this.value = result
     this.fullfillArr.forEach(fn => {
      fn()
     })
   }, 0)
 }
 let reject = reason => {
   if (this.state !== 'pending') return
   setTimeout(() => { // 同步时插入,保证回调函数被异步执行
     this.state = 'rejected'
     this.value = reason
     this.rejectedArr.forEach(fn => {
       fn()
    })
   }, 0)
 }
 try {
   excutor(resolve, reject)
 } catch (error) {
   reject(error)
 }
}   

可以看到Promise中有

1.1 几个私有成员

state

中存储状态值,有三种,实例化时的'pending',被resolve或reject函数调用时被决定为fullfilled或rejected。

value

中存储状态改变函数resolvet所传过来的值。

reason

中存储状态改变函数reject所传来的值。

1.2 执行器函数excutor

主要作用是进行一些同步异步操作然后在异步的回调函数中调用状态改变函数,或者是同步结束时调用状态改变函数。当然你也可以啥都不调用,这样也就没有后续被添进callbackArray中的大伙们啥事了。

excutor为Promise的形参,其实参为new Promise((resolve, reject) => { resolve(233) })中的(resolve, reject) => { resolve(233) },即一个定制的函数。

该函数有两个参数: resolve, reject,两个形参在excutor被调用时实参分别对应Promise的内部函数resolve, reject。

而resolve和reject内部函数且看下面

1.3 执行器函数的两个参数

是两个函数,作用是在将需要改变promise对象状态时将其状态改变,并且传入一个所需的值赋值给promiseX.value。

在promise的excutor中被执行时的两个参数被传入Promise内部函数resolve, reject,在被调用时实参为其传来的值,如上栗中的233。

好了看完这些,你大概知道Promise和他的执行器都在干嘛了,大概核心就是实例化一个对象,该对象拥有几个拥有默认值的私有成员:状态,值...等等,然后再执行一下excutor执行器函数,这个时候根据需求将对象的状态值决定下来并且将某个传来的值保存下来。

好了,大体就做了这些,那么做了这些才只是开始呀,我们用Promise不就是想代替回调地狱吗,链式调用的时候怎么实现,值怎么传过去,什么时候执行定义好的下一个步骤,这些看下一节核心方法:then的实现。

二 Promise.prototype.then

then是干嘛的? 指定promise对象在决定态(onFullfilled, onRejected)时多执行的回调函数

所以then是一个函数,其形参有两个,onFullfilled ,onRejected,第二个参数为可选。其实参为我们实际定制时所定义的函数。

eg:

new Promise((resolve, reject) => {
 resolve('fullfilled')
})
.then(
(res) => {console.log(res)},
(reason) => {coonsole.log(reason)}
)

上个栗子中的(res) => {console.log(res)}即为onFullfilled对应的实参, (reason) => {coonsole.log(reason)}即为onRejected对应的实参。它只是个函数的引用,所以不要看着一大串就以为在执行,实际上这两个函数并没有在上述的同步代码中执行,为什么呢,看下面

2.1 异步执行与同步then

为了能够使同一个promise在状态决定后去同步执行多个步骤,比如A动画完了后要求加载B,C动画,且BC动画因为其后续场景不同的原因不应该放置一个函数中,那么就有了一个promise多次,then同步执行的需求了。则异步执行就很重要了,需要在本次栈内代码执行完多个then添加后再去进行状态更改并且将该promise下的then全部执行。啧啧,发布订阅的感觉吼。不一样的是,错过后该次改变后再添加同样能得到执行。

本文里使用了setTimeout异步宏任务来实现异步,从而使状态值更改时所有的then回调都已经添加到位从而被全部执行。

同时,promise对象中要有两个回调函数数组fufillArr,rejectArr来储存每个then中的onfullFilled和onRejected,其添加(push)时机当然是在then函数中了。这里就对应了本节第一句主题:指定promise对象在决定态(onFullfilled, onRejected)时多执行的回调函数。

2.2 then的链式

写之前不妨先想想,链式什么意思,不就是在回调中再去调用一个回调的意思吗,那么在异步回调需求出现时,我们不就又需要一个状态机器Promise了么,马上给他来个return new Promise(....)操作。ok,这不就完事了。个锤子了。

好了你有了promise对象返回知道怎么决定后续的状态了,那你还么得做上面的要求push呢,于是我们将push操作放在了这个要new 的Peomise的excutor中,这样的意义在于后续的回调决定于前一轮push进去的回调的执行结果。

到这里,一个简易的promise以及then的实现过程就说的差不多了当然中间又很多判断以及普通值情况判断就不细说了一切皆在代码中,那么上一下then 的代码。

// 格式又挂了,直接看下面github上代码吧 
Promise.prototype.then = function (onfullFilled, onRejected) {                                 typeof onfullFilled !== 'function' ? onfullFilled = result => result : null;            typeof onRejected !== 'function' ? onRejected = reason => {                throw new Error(reason instanceof Error ? reason.message : reason);            } : null                                         return new Promise((resolve, reject) => {                                                this.fullfillArr.push(() => {                    try {                        let onfullFilledReturn = onfullFilled(this.value)                                                onfullFilledReturn instanceof Promise ? onfullFilledReturn.then(resolve, reject) : resolve(onfullFilledReturn)                    } catch (error) {                        reject(error)                    }                })                                                                       this.rejectedArr.push(() => {                    try {                        let onRejectedReturn = onRejected(this.value)                        onRejectedReturn instanceof Promise ? onRejectedReturn.then(resolve, reject) : resolve(onRejectedReturn)                    } catch (error) {                        reject(error)                    }                })                            })      }

最后挂一下整体的实现以及验证链接: promiseComeTrue.html

三 Promise.prototype.all and Promise.prototype.race

也是写实现,不是很难,大家自己写下吧,我明天再写。困。。

参考

Promise 对象

promise-polyfill

PromiseA+

我如何实现Promise A+

扒一扒PROMISE的原理,大家不要怕!

Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)