星不克

星不克 查看完整档案

杭州编辑河南科技大学  |  大前端一统江湖 编辑某公司  |  BUG开发工程师 编辑 segmentfault.com/u/dugujiujian18 编辑
编辑

一蓑烟雨任平生

个人动态

星不克 提出了问题 · 1月14日

紧急求助!编辑文章标题用的是三个'#',右侧目录就是展示不出来,而且我发现我打开任何一篇文章都没有目录,怎么回事呢?

如下图,我编辑时标题前都加了三个“#”,但是右侧没有生成目录:
image

关注 1 回答 0

星不克 赞了文章 · 1月12日

一步一步实现自己的Promise

倘若你对Promise的使用还不是很熟练,我并不建议你就来实现自己的Promise,
你可以先阅读
【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)

一步一步实现Promise的源码地址:点击进入

整体流程的介绍

  1. 先定义整体结构
  2. 实现Promise构造函数
  3. 实现promise.then
  4. 实现promise.catch
  5. 实现promise.resovle
  6. 实现promise.reject
  7. 实现promise.all
  8. 实现promise.race

1.定义整体结构

  1. 先写出构造函数,将Promise向外暴露
/*
自定义Promise函数模块:IIFE
 */

(function (window) {
    /*
    Promise构造函数
    executor:执行器函数
     */
    function Promise(executor) {

    }

    // 向外暴露Promise
    window.Promise = Promise
})()
  1. 添加Promise原型对象上的方法
 /*
    Promise原型对象的then
    指定一个成功/失败的回调函数
    返回一个新的promise对象
     */
    Promise.prototype.then = function(onResolved,onRejected){

    }

    /*
    Promise原型对象的.catch
    指定一个失败的回调函数
    返回一个新的promise对象
     */
    Promise.prototype.catch = function(onRejected){

    }
  1. 添加Promise函数对象上的方法
/*
    Promise函数对象的resovle方法
    返回一个指定结果的promise对象
     */
    Promise.resolve = function(value){

    }

    /*
    Promise函数对象的reject方法
    返回一个指定reason的失败状态的promise对象
    */
    Promise.reject = function(value){

    }

    /*
    Promise函数对象的all方法
    返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
    */
    Promise.all = function(value){

    }

    /*
    Promise函数对象的race方法
    返回一个promise对象,状态由第一个完成的promise决定
    */
    Promise.race = function(value){

    }

2. 实现Promise构造函数

  1. 众所周知,构造函数里由resolve和reject方法,而且这两个方法会被传入executor,并且executor立即同步执行
    /*
    Promise构造函数
    executor:执行器函数
     */
    function Promise(executor) {

        function resovle() {

        }
        function reject() {

        }

        // 立即同步执行executor
        executor(resovle,reject)
    }
  1. 家喻户晓的是,每个promise都有一个状态可能为pending或resolved,rejected。因此需要添加个status,此外,当我们这样使用Promise的时候,
// 例1
var promise = new Promise((resovle,reject)=>{
    
})

promise.then(resolve=>{},reject=>{})

这时执行到then,promise的状态还是pending,这时要把值和then里面的回调函数保存起来,所以需要个data和callbacks

function Promise(executor) {

        var self = self

        self.status = 'pending' // 给promise对象指定status属性,初始值为pending
        self.data = undefined // 给promise对象指定一个存储结果的data
        self.callbacks = []  // 每个元素的结构:{onResolved(){},onRejected(){}}


        function resovle() {

        }
        function reject() {

        }

        // 立即同步执行executor
        executor(resovle,reject)
    }
  1. 妇孺皆知的是,在上面的例子1的基础上,当我们执行resovle(value)时,
// 例2
var promise = new Promise((resolve,reject)=>{
    setTimeout(function () {
        resolve(1)
    })
})

promise.then(resolve=>{},reject=>{})

会把promise对象的status改为resovle,并且把value保存到data,然后执行之前保存的callbacks(上面说过当执行到then时,发现当前的promise是pending状态,会把then里的回调函数保存到promise的callbacks里)。

function resolve(value) {
            // 如果当前状态不是pending,则不执行
            if(self.status !== 'pending'){
                return
            }
            // 将状态改为resolved
            self.status = 'resolved'
            // 保存value的值
            self.data = value

            // 如果有待执行的callback函数,立即异步执行回调函数onResolved
            if (self.callbacks.length>0){
                setTimeout(()=>{
                    self.callbacks.forEach(callbackObj=>{
                        callbackObj.onResolved(value)
                    })
                })
            }
        }
  1. 我们还知道,promise的状态只能改变一次,因此当执行resolve的时候要判断是不是promise是不是pending的状态,否则是不能执行的
function resolve(value) {
            // 如果当前状态不是pending,则不执行
            if(this.status !== 'pending'){
                return 
            }
            // 将状态改为resolved
            this.status = 'resolved'
            // 保存value的值
            this.data = value

            // 如果有待执行的callback函数,立即异步执行回调函数onResolved
            if (this.callbacks.length>0){
                setTimeout(()=>{
                    this.callbacks.forEach(callbackObj=>{
                        callbackObj.onResolved(value)
                    })
                })
            }
        }
  1. 异曲同工之妙的是reject方法也是这个道理,因此这里无需赘述
function reject(value) {
            // 如果当前状态不是pending,则不执行
            if(self.status !== 'pending'){
                return
            }
            // 将状态改为resolved
            self.status = 'rejected'
            // 保存value的值
            self.data = value

            // 如果有待执行的callback函数,立即异步执行回调函数onResolved
            if (self.callbacks.length>0){
                setTimeout(()=>{
                    self.callbacks.forEach(callbackObj=>{
                        callbackObj.onRejected(value)
                    })
                })
            }
        }
  1. 我们又知道,当在执行executor的时候,如果执行异常的话,这个promise的状态会直接执行reject方法。
// 例 3
var promise = new Promise((resolve,reject)=>{

    执行到这里出错了

    setTimeout(function () {
        resolve(1)
    })
})

要实现这个功能,我们可以在executor外让try catch来捕获

        try{
            // 立即同步执行executor
            executor(resolve,reject)
        }catch (e) { // 如果执行器抛出异常,promise对象变为rejected状态
            reject(e)
        }

好了,现在来测试一下,为了测试,我们需要简单的实现下then,让它往callbacks里push then里的回调函数

   Promise.prototype.then = function(onResolved,onRejected){
        // 假设当前状态还是pending状态,将回调函数保存起来
        this.callbacks.push({
            onResolved,
            onRejected
        })
    }

好了,现在测试下,发现成功。

 // 例4
 let promise = new Promise((resolve,reject)=>{
        
        setTimeout(function () {
            // resolve(1)
            reject(1)
        },100)
    })

    promise.then(
        value=>{
            console.log("onResolved:",value);
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )

3. 实现then方法

尽人皆知的时,执行到then时,promise可能会是pending状态,此时就要把then里的回调函数保存起来,也可能会是resolved或者rejected状态,此时要执行onResolved或onRejected方法。

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        if(self.status === 'pending'){
            // promise当前状态还是pending状态,将回调函数保存起来
            self.callbacks.push({
                onResolved(){onResolved(self.data)},
                onRejected(){onRejected(self.data)}
            })
        }else if(self.status === 'resolved'){
            setTimeout(()=>{
                onResolved(self.data)
            })
        }else{
            setTimeout(()=>{
                onResolved(self.data)
            })
        }

    }

而且我们知道,执行完then是要返回一个新的promise的,而新的promise的状态则由当前then的执行结果来确定。

 Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise当前状态还是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }

当当前的promise状态为resolved的时候,则当前执行的onresolved函数由三种情况

  1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
// 例5
    let promise = new Promise((resolve,reject)=>{
        resolve(1)
    })

    promise.then(
        value=>{
            return value
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )

因此,我们可以这样实现

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise当前状态还是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    const result = onResolved(self.data)
                    if (result instanceof Promise){

                    } else {
                    // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                        resolve(result)
                    }
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }
  1. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果,如代码所示,我们返回一个新的promise。这个新的promise执行了resolve,因此返回的promise的状态是resolved的
// 例6
 let promise = new Promise((resolve,reject)=>{
        resolve(1)
    })

    promise.then(
        value=>{
            return new Promise((resolve,reject)=>{
                resolve(2)
            })
        },
        reason=>{
            console.log("onRejected:",reason);
        }
    )

因此我们可以这样实现

Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
            if(self.status === 'pending'){
                // promise当前状态还是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(){onResolved(self.data)},
                    onRejected(){onRejected(self.data)}
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    const result = onResolved(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                        resolve(result)
                    }
                })
            }else{
                setTimeout(()=>{
                    onResolved(self.data)
                })
            }
        })

    }

在这里说明一下:

result.then(
    value => {resolve(value)},
    reason => {reject(reason)}
)

由于我们在例6中执行了then里的resolve函数,
将会导致value => {resolve(value)},这个回调函数的执行,因此会把即将返回的新的promise的data设置为value,会把状态设置为resolved。

  1. 如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected,我们可以用try catch来实现
setTimeout(()=>{
    try{
        const result = onResolved(self.data)
        if (result instanceof Promise){
            // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
            result.then(
                value => {resolve(value)},
                reason => {reject(reason)}
            )
        } else {
            // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
            resolve(result)
        }
    }catch (e) {
      //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
        reject(e)
    }
})

异曲同工之妙的是当当status === 'rejected',道理一样

 setTimeout(()=>{
      try{
          const result = onRejected(self.data)
          if (result instanceof Promise){
              // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
              result.then(
                  value => {resolve(value)},
                  reason => {reject(reason)}
              )
          } else {
              // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
              resolve(result)
          }
      }catch (e) {
          //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
          reject(e)
      }
  })

到这里,我们发现当执行resolve的时候,onResolved(self.data)onRejected(self.data)执行时也会跟上面一样的结果,可以说执行回调函数都要做以上判断,因此我们要将

 self.callbacks.push({
    onResolved(){onResolved(self.data)},
    onRejected(){onRejected(self.data)}
})

改成

if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
    onResolved(){
        try{
            const result = onResolved(self.data)
            if (result instanceof Promise){
                // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                result.then(
                    value => {resolve(value)},
                    reason => {reject(reason)}
                )
            } else {
                // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                resolve(result)
            }
        }catch (e) {
            //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
            reject(e)
        }
    },

到此,我们发现,相同的代码太多了,因此有必要封装一下

 function handle(callback) {
    try{
        const result = callback(self.data)
        if (result instanceof Promise){
            // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
            result.then(
                value => {resolve(value)},
                reason => {reject(reason)}
            )
        } else {
            // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
            resolve(result)
        }
    }catch (e) {
        //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
        reject(e)
    }
}

这样以来就清爽了很多

   Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject)=>{
           /*
            调用指定回调函数的处理,根据执行结果。改变return的promise状态
             */
            function handle(callback) {
                try{
                    const result = callback(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                        resolve(result)
                    }
                }catch (e) {
                    //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
                    reject(e)
                }
            }
            if(self.status === 'pending'){
                // promise当前状态还是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(){
                        handle(onResolved)
                    },
                    onRejected(){
                        handle(onRejected)
                    }
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    handle(onResolved)
                })
            }else{ // 当status === 'rejected'
                setTimeout(()=>{
                    handle(onRejected)
                })
            }
        })

    }

另外,我们还知道,promise会发生直传透,例如

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

运行结果: 1

解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,当传入then的不是函数的时候,这个then是无效的。

因此,要实现直传透这个特性,我们可以这样实现

添加这两句来判断要不要发生值传透

onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
 Promise.prototype.then = function(onResolved,onRejected){
        onResolved = typeof onResolved === 'function'? onResolved: value => value
        onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
        var self = this

        return new Promise((resolve,reject)=>{

            /*
            调用指定回调函数的处理,根据执行结果。改变return的promise状态
             */
            function handle(callback) {
                try{
                    const result = callback(self.data)
                    if (result instanceof Promise){
                        // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                        result.then(
                            value => {resolve(value)},
                            reason => {reject(reason)}
                        )
                    } else {
                        // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                        resolve(result)
                    }
                }catch (e) {
                    //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
                    reject(e)
                }
            }
            if(self.status === 'pending'){
                // promise当前状态还是pending状态,将回调函数保存起来
                self.callbacks.push({
                    onResolved(){
                        handle(onResolved)
                    },
                    onRejected(){
                        handle(onRejected)
                    }
                })
            }else if(self.status === 'resolved'){
                setTimeout(()=>{
                    handle(onResolved)
                })
            }else{ // 当status === 'rejected'
                setTimeout(()=>{
                    handle(onRejected)
                })
            }
        })

    }

3.实现catch方法

妇孺皆知的是,catch方法的作用跟then里的第二歌回调函数一样,因此我们可以这样来实现

Promise.prototype.catch = function(onRejected){
    return this.then(undefined,onRejected)
}

天啊,居然这么简单

4. 实现Promise.resolve

我们都知道,Promise.resolve方法可以传三种值

  1. 不是promise
  2. 成功状态的promise
  3. 失败状态的promise

    Promise.resolve(1)
    Promise.resolve(Promise.resolve(1))
    Promise.resolve(Promise.reject(1))

实际上跟实现上面的then时有点像

Promise.resolve = function(value){
  return new Promise((resolve,reject)=>{
      if (value instanceof Promise){
          // 如果value 是promise
          value.then(
              value => {resolve(value)},
              reason => {reject(reason)}
          )
      } else{
          // 如果value不是promise
          resolve(value)
      }

  }

}

5.实现Promise.reject

实现这个比较简单,返回一个状态为rejected的promise就好了

/*
Promise函数对象的reject方法
返回一个指定reason的失败状态的promise对象
*/
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}

6.实现Promise.all

我们知道,这个方法会返回一个promise

    /*
    Promise函数对象的all方法
    返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
    */
    Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
           
        })
    }
  

而这个promise的状态由遍历每个promise产生的结果决定

    /*
    Promise函数对象的all方法
    返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
    */
    Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
            // 遍历promises,获取每个promise的结果
            promises.forEach((p,index)=>{
                
            })
        })
    }

有两种结果:

  1. 遍历到有一个promise是reject状态,则直接返回的promise状态为rejected
 Promise.all = function(promises){
        return new Promise((resolve,reject)=>{
            // 遍历promises,获取每个promise的结果
            promises.forEach((p,index)=>{
                p.then(
                    value => {
    
                    },
                    reason => { //只要有一个失败,return的promise状态就为reject
                        reject(reason)
                    }
                )
            })
        })
    }
  1. 遍历所有的promise的状态都为resolved,则返回的promise状态为resolved,并且还要每个promise产生的值传递下去
   Promise.all = function(promises){
      const values = new Array(promises.length)
      var resolvedCount = 0 //计状态为resolved的promise的数量
      return new Promise((resolve,reject)=>{
          // 遍历promises,获取每个promise的结果
          promises.forEach((p,index)=>{
              p.then(
                  value => {
                      // p状态为resolved,将值保存起来
                      values[index] = value
                      resolvedCount++;
                      // 如果全部p都为resolved状态,return的promise状态为resolved
                      if(resolvedCount === promises.length){
                          resolve(values)
                      }
                  },
                  reason => { //只要有一个失败,return的promise状态就为reject
                      reject(reason)
                  }
              )
          })
      })
  }

好像可以了,当其实这里还有一个问题,就是all传进去的数组不一定都是promise对象,可能是这样的

all([p,2,3,p])

因此需要把不是promise的数字包装成promise

    Promise.all = function(promises){
        const values = new Array(promises.length)
        var resolvedCount = 0 //计状态为resolved的promise的数量
        return new Promise((resolve,reject)=>{
            // 遍历promises,获取每个promise的结果
            promises.forEach((p,index)=>{
                Promise.resolve(p).then(
                    value => {
                        // p状态为resolved,将值保存起来
                        values[index] = value
                        resolvedCount++;
                        // 如果全部p都为resolved状态,return的promise状态为resolved
                        if(resolvedCount === promises.length){
                            resolve(values)
                        }
                    },
                    reason => { //只要有一个失败,return的promise状态就为reject
                        reject(reason)
                    }
                )
            })
        })
    }

7.实现Promise.race

这个方法的实现要比all简单很多

  /*
    Promise函数对象的race方法
    返回一个promise对象,状态由第一个完成的promise决定
    */
    Promise.race = function(promises){
        return new Promise((resolve,reject)=>{
            // 遍历promises,获取每个promise的结果
            promises.forEach((p,index)=>{
                Promise.resolve(p).then(
                    value => {
                        // 只要有一个成功,返回的promise的状态九尾resolved
                        resolve(value)

                    },
                    reason => { //只要有一个失败,return的promise状态就为reject
                        reject(reason)
                    }
                )
            })
        })
    }

查看原文

赞 1 收藏 1 评论 0

星不克 赞了回答 · 1月11日

解决思否上写的文章如何展示目录?文章中用的是三个'#',目录就是展示不出来,而且我打开任何一篇文章都没有目录,捉急!!!

有的,只是没有默认展开,要手动展开,不明显。我也是很久之后才发现有目录。
image.png
image.png

image.png
image.png

关注 2 回答 1

星不克 赞了回答 · 1月11日

解决思否上我自己写的文章忽然不能编辑了

在底部吧:
image.png

关注 1 回答 1

星不克 提出了问题 · 1月8日

解决思否上我自己写的文章忽然不能编辑了

image
如上图所示:我自己写的文章今天忽然发现没有'编辑'入口,真是郁闷,难道文章都要一次成型吗。。。

关注 1 回答 1

星不克 关注了专栏 · 1月8日

前端巅峰

注重前端性能优化和前沿技术,重型跨平台开发,即时通讯技术等。 欢迎关注微信公众号:前端巅峰

关注 20099

星不克 关注了专栏 · 1月8日

SegmentFault 行业快讯

第一时间为开发者提供行业相关的实时热点资讯

关注 54185

星不克 关注了专栏 · 1月8日

进击的大前端

前端工程师,底层技术人。 思否2020年度“Top Writer”! 掘金“优秀作者”! 开源中国2020年度“优秀源创作者” 分享各种大前端进阶知识! 关注公众号【进击的大前端】第一时间获取高质量原创。 更多文章和示例源码请看:https://github.com/dennis-jiang/Front-End-Knowledges

关注 10000

星不克 关注了专栏 · 1月8日

SegmentFault 社区运营

SegmentFault 思否社区管理官方专栏,在这里发布 SegmentFault 思否社区运营和管理相关的最新消息。

关注 17897

星不克 关注了用户 · 1月8日

LNMPRG源码研究 @php7internal

一群热爱代码的人 研究Nginx PHP Redis Memcache Beanstalk 等源码 以及一群热爱前端的人
希望交流的朋友请加微信 289007301 注明:思否 拉到交流群,也可关注公众号:LNMPRG源码研究

《PHP7底层设计与源码分析》勘误https://segmentfault.com/a/11...

《Redis5命令设计与源码分析》https://item.jd.com/12566383....

景罗 陈雷 李乐 黄桃 施洪宝 季伟滨 闫昌 李志 王坤 肖涛 谭淼 张仕华 方波 周生政 熊浩含 张晶晶(女) 李长林 朱栋 张晶晶(男) 陈朝飞 巨振声 杨晓伟 闫小坤 韩鹏 夏达 周睿 李仲伟 张根红 景罗 欧阳 孙伟 李德 twosee

关注 11606

认证与成就

  • 获得 10 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 5 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-02-28
个人主页被 704 人浏览