美文网首页
手写 Promise 系列 --- 1

手写 Promise 系列 --- 1

作者: 人话博客 | 来源:发表于2019-06-26 14:13 被阅读0次

Promise 是 ES6 推出的用于解决callback嵌套层级太深问题的一种异步方案.

基本使用如下.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

myPromise.then(data=>{
    console.log(data)
},err=>{
    console.log(err)
})

先不考虑那么多,也不像很多博客里写的那种,一上来就给非常完美的思路和非常标准的 Promise/A+ 规范.

我就按照自己思路一点点的来做.


step 01 从代码代用级别摸清内部构造

  • 一个 Promise 对象,有三个状态.
  • 初始状态是 pending.
  • 成功状态是 resolve.
  • 失败状态是 reject.
image.png

然后看上述写的代码.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

  • new Promise () 是一个构造函数,没什么好说的.
function MyPromise () {

}

// 肯定是 
let my_promise = new MyPromise()

  • 这个构造函数接受的参数也是一个函数,也没什么好说的.
function MyPromise (func) {
    typeof func === 'function' ===> true
}
  • 在这个函数参数里我们声明了 resolve 和 reject 两个形参.
function MyPromise((resolve,reject) => {
    typeof reject === 'function'  // true
    typeof resolve === 'function' // true
})
  • 在后续的使用参数的过程中,我们可以知道,是构造函数传入的执行器,在内部调用了这两个reject 和 resolve
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
        reslove(data) // or reject (err)
    },1000)
})

  • reject 和 resolve 肯定不是由传递进去的参数自己提供的.
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
    // resovle 哪来的?
        reslove(data) // or reject (err) // reject 哪来的?
    },1000)
})

  • 那么就只有一个可能,那是 Promise 内部提供的两个功能函数.
function MyPromise () {
    function resolve () {}
    function reject () {}
}
  • 其中 resolve 函数别的不管,起码应该会把当前 promise 对象的状态由 pending --> resolve reject 同理, 从 pending --> reject
function MyPromise () {
    function resolve () {
        // pending ---> resolve
    }
    function reject () {
        // pending ---> reject
    }
}

  • 调用 then 实例方法,会将两种状态的回调函数传递进去.
new MyPromise(executor).then(resolveData=>{},rejectData=>{})

step 02 根据上述推理,猜测内部写法.

1. 首先是一个构造函数

function MyPromise () {

}

2. 其次需要接受一个执行器.

function MyPromise (executor) {
    
}

3. 实例化出来的 Promise 对象有三种状态

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

4. 一开始 promise 对象是 pending 状态

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
}

5. 内部起码得有两个 reject 和 resolve.要不然executor里面的reject 和 resolve 哪来的? 或者把这两个方法

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
    const that = this // 挂载 this
    function resolve () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.PENDING
        }
    }
    
    function reject () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.REJECT
        }
    }
}

或者觉得挂载 this 麻烦,我就把 resolve 和 reject 挂在 prototype 上.

MyPromise.prototype.reject = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

MyPromise.prototype.resolve = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

到目前为止,我们可以通过 executor 内部调用 reject 或者 resolve 修改某个 Promise 的状态了.

在回到我们使用 ES6 的 Promise 代码中.

new Promise ((resolve,reject) => {
    resolve(someData) // or reject(someError)
}) 

可以知道 reslove 和 reject 是可以接受参数的.

  • 对于 resolve 来说,参数就是 executor 执行成功返回的的值.
  • 对于 reject 来说,参数就是 executor 执行失败返回的值.

6. 给 reject 和 resolve 来设置一个形参

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

7.我们通过 then 传递了两个函数,第一个是成功的回调. 第二个是失败的回调.

MyPromise.prototype.then = function (onResolve,onReject) {
    this.onResloveCallback = onResolve
    this.onRejectCallback = onReject
}


在回到 ES6 的 Promise 调用代码中.

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve(data形参) // reject(失败形参)
    },1000)
})

p.then(data=>{
    data === data 形参
})

8.resolve的形参传递给了then的第一个回调函数的参数,error传递给了then的第二个回调函数的参数

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
        this.error = error
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
        this.data = data
    }
}


9. 状态改变完成之后,就需要调用从then函数接受过来的两个函数参数了(reject&resolve)

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 该状态
        this.error = error // 记录值
        this.onRejectCallback(this.error) // 调用回调
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改状态
        this.data = data // 记录值
        this.onResolveCallback(data) // 调用回调
    }
}

完整的代码

function MyPromise (executor) {
    this.state = this.states.PENDING
    this.value = null // 用于记录executor内部的reject or resolve 传递回来的值.
    this.onRejectCallback = null
    this.onResolveCallback = null
    
    
    executor(this.resolve.bind(this),this.reject.bind(this))

}

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 该状态
        this.error = error // 记录值
        this.onRejectCallback(this.error) // 调用回调
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改状态
        this.data = data // 记录值
        this.onResolveCallback(data) // 调用回调
    }
}



step 03 . 测试MyPromise 的 resolve 和 reject

测试调用自定义的promise ---> pending --> resolve

new MyPromise((resolve, reject) => { 
  setTimeout(() => {
    resolve(1)
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

正确输出.

image.png

测试调用自定义的promise ---> pending --> reject

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('error')
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

错误输出.

image.png

第一阶段手写 Promise 达成目标:

  • 状态改变 [✅]
  • 正确的调用then的函数接受的两个回调函数.[✅]

补充一点:

如果把 reject 和 resolve 写在 prototype 上,然后直接通过 this.reject/resolve 传递会导致 this 指向的错误(obj.method当函数传递,只能传 method, 不会传 obj,真正的纯函数传递).所以,上述代码一定要写成.

executor(this.resolve.bind(this),this.reject.bind(this))

相关文章

  • 手写 Promise 系列 --- 3

    在前两篇(手写 Promise 系列 --- 1)和(手写 Promise 系列 ---2) 中,达成了3个目标 ...

  • 手写 Promise 系列 --- 1

    Promise 是 ES6 推出的用于解决callback嵌套层级太深问题的一种异步方案. 基本使用如下. 先不考...

  • 手写 Promise 系列 --- 2

    在上一篇 手写 Promise 系列 --- 1 中,我们达成了两个目标 Promise 实例的状态改变 [✅] ...

  • 手写Promise

    手写 Promise 我们会通过手写一个符合 Promise/A+ 规范的 Promise 来深入理解它,并且手写...

  • 手写基础 promise

    1. 前言 玩下吧 手写 promise,看看能写成啥样 2. promise 基础结构 3. 手写`promi...

  • 手写promise(1)

    class Promise{ constructor(excutorCallBack){ this.status ...

  • 2020前端基础大纲(20200202)

    2020前端基础大纲1、promise 原理 promise.all 可以手写出大概。(async awa...

  • 手写Promise

    $ 正常的promise用法   $ 手写的Promise   # 测试可行性

  • 手写promise

    手写promise 带大家手写一个 promis。在手写之前我会先简单介绍一下为什么要使用promise、prom...

  • 前端手写算法题

    1、深拷贝deepCopy 2、对象扁平化 3、数组扁平化 4、手写Promise 5、promise.all方法...

网友评论

      本文标题:手写 Promise 系列 --- 1

      本文链接:https://www.haomeiwen.com/subject/surccctx.html