面试官你来,130行带你手写完整Promise

大家好,我是雷锋蜀黍。一直在某些面试宝典看到面试官要你手撸一个promise,今天天气这么好,不如我们来搞一搞。(PS:从未看过别人的实现,本文更像是记录一个思考的过程)

最终的代码完全符合Promise A+规范,并通过所有 promises-aplus-tests测试用例。先上个测试通过图得瑟一下,嘿嘿嘿。

如果自己动手实现过Promise的可以直接跳到 从零开始的Promise 章节,看看我的实现,下面我们开始吧。

分析

先回想一下promise是个啥:promise是为了解决回调地狱而出现的产物,通过实例的then、catch方法进行链式调用。它本身拥有三种状态:等待、成功、失败,并且成功失败后状态无法改变。一般使用方式:

new Promise(function (resolve, reject) {    setTimeout(function () {     resolve(1)    }, 1000)}).then(function (res) {    console.log(res) // 1    return res}).catch(function (res) { // 不会被调用    console.log(res)    return res})

浏览器打印:

从中我们可以看到实例上具有[[PromiseStatus]][[PromiseValue]]两个属性,分别代表状态与值。

隐式原型上具有catch、finally、then方法。

轻微试探

先照上面分析的打个样:

let MyPromise = (function () {    function resolve(res) {        this['[[PromiseStatus]]'] = 'fulfilled'        this['[[PromiseValue]]'] = res    }    function reject(res) {        this['[[PromiseStatus]]'] = 'rejected'        this['[[PromiseValue]]'] = res    }    return function (fn) {        this['[[PromiseStatus]]'] = 'pending'        this['[[PromiseValue]]'] = undefined        fn(resolve.bind(this), reject.bind(this))     }})()MyPromise.prototype.then = function (fn) {    // 只有在成功状态才会调用    if (this['[[PromiseStatus]]'] === 'fulfilled') {        this['[[PromiseValue]]'] = fn(this['[[PromiseValue]]'])    }    // 返回自身 进行链式调用    return this}MyPromise.prototype.catch = function (fn) {    // 只有在失败状态才会调用    if (this['[[PromiseStatus]]'] === 'rejected') {        this['[[PromiseValue]]'] = fn(this['[[PromiseValue]]'])    }    // 返回自身 进行链式调用    return this}

试着打印输出:

let a = new MyPromise(function (resolve, reject) {    setTimeout(function () {        resolve(1)    }, 1000)}).then(function (res) {    console.log(res)    return 2}).catch(function (res) {    console.log(res)    return 3})console.log(a)

事情不简单

我们创建个属性进行链式函数的存储,需要注意的是存在成功和失败两种状态。

let MyPromise = (function () {    function resolve(res) {        this['[[PromiseStatus]]'] = 'fulfilled'        this['[[PromiseValue]]'] = res       // 模拟异步 并保证接收到所有then方法后执行        setTimeout(() => {               // 遍历finishArr并链式执行            while (this.asyncArr.finishArr.length > 0) {                this.asyncArr.finishArr.shift()()            }        }, 0);        // 干掉永远不可能执行的方法        this.asyncArr.errorArr = []    }    function reject(res) {        this['[[PromiseStatus]]'] = 'rejected'        this['[[PromiseValue]]'] = res       setTimeout(() => {       // 遍历errorArr并链式执行            while (this.asyncArr.errorArr.length > 0) {                this.asyncArr.errorArr.shift()()            }        }, 0);        // 干掉永远不可能执行的方法        this.asyncArr.finishArr = []    }    return function (fn) {        this['[[PromiseStatus]]'] = 'pending'        this['[[PromiseValue]]'] = undefined        this.asyncArr = {       // 保存链式调用的方法            finishArr: [],            errorArr: []        }        fn(resolve.bind(this), reject.bind(this))    }})()MyPromise.prototype.then = function (fn) {    // 失败时直接返回 等待和成功都要压入数组中    if( this['[[PromiseStatus]]'] === 'rejected')  return this    this.asyncArr.finishArr.push( ()=> {        this['[[PromiseValue]]'] = fn(this['[[PromiseValue]]'])    })    // 返回自身 进行链式调用    return this}MyPromise.prototype.catch = function (fn) {    if( this['[[PromiseStatus]]'] === 'fulfilled')  return this    this.asyncArr.errorArr.push( ()=> {        this['[[PromiseValue]]'] = fn(this['[[PromiseValue]]'])    })    // 返回自身 进行链式调用    return this}

欧凯~这样就解决了异步的问题,也完全满足我们常用的几个功能。但是大家觉得这就结束了吗?不,这只是开始,还是个错误的开始。

大错特错

下面我们一起来总结下上面的代码在逻辑上存在哪些错误

1.promise的状态无法被“改变”

可能有人就疑惑了,promise的状态本来就是不能被改变的,这没有错啊。那请你来看下面这段代码:

let a = new Promise(function (resolve, reject) {
        reject(1)
    })
    .catch(function (res) {
        console.log(res)
        return 3
    })
    .then(function (res) {
        console.log(res)
    })
    console.log(a)  // type: resolved    val: undefined

你会发现a的状态从reject变为了resolved,你是否会动摇对promise的认知呢,就觉得别人说的什么promise不能改变状态都是扯淡,能变得。

切勿听风是雨,自己动手试一下才会真的明白。 then方法返回的并不是当前对象自身,而是一个全新的Promise。

2.鸡蛋全放一个篮子里

现在我们将所有的then、catch方法都存放到了第一个promise的属性上,那如果按照上一条的改进意见,每次都返回一个全新的promise,那这种方式是否可行呢?答案我想是可以的,但是这样并不好,会造成头重脚轻,首个promise对象与其他promise对象大不一样的感觉,所以我想正确的数据结构应该是这样的。

从零开始的Promise

下面才是本篇文章正真的开始,根据以上总结出的经验,我们来实现一个完整的并完全符合Promise A+规范的Promise。

我们先来总览一下MDN上描述的Promise方法:

静态方法:

Promise.all(iterable)

Promise.race(iterable)

Promise.reject(reason)

Promise.resolve(value)

实例方法:

Promise.prototype.catch(onReject)

Promise.prototype.then(onFulfilled,onRejected)

Promise.prototype.finally

具体细节就不赘述了,不太清楚的小伙伴请自行去mdn上进行学习后再回来看具体实现,下面我们开始吧。

Promise构造函数

人狠话不多,先上代码再讲解

let MyPromise = (function () {
    function resolve(res) {
        // 如果存在状态则直接返回
        if (this['[[PromiseStatus]]'] != 'pending') return  
        // 如果接收到的值是自己本身则报错
        if (res === this) {
            throw new Erroe(
            'TypeError: Chaining cycle detected for promise #<Promise>')
        }
        // 如果res 是一个promise对象 则等待其值然后返回
        this['[[PromiseStatus]]'] = 'fulfilled'
        this['[[PromiseValue]]'] = res

        // promise 是一个宏任务  使用 setTimeout 进行模拟
        setTimeout(() => {
            if (this.next) {
                this.next()
                delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
            }
        }, 0)
    }
    function reject(res) {
        // 如果存在状态则直接返回
        if (this['[[PromiseStatus]]'] != 'pending') return  
        this['[[PromiseStatus]]'] = 'rejected'
        this['[[PromiseValue]]'] = res
        setTimeout(() => {
            // 如果有下一个promise则传值,如果没有则直接报错
            if (this.next) {
                this.next()         //  将会创建下一个promise 在then方法中进行定义
                delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
            } else {
                throw new Error(res)
            }
        }, 0)
    }
    return function (fn) {
        this['[[PromiseStatus]]'] = 'pending'   // 保存当前promise状态
        this['[[PromiseValue]]'] = undefined    // 保存当前promise的值
        this.next = null                        // 创建下一个promise
        // 保存所有的finally方法 当改promise当用完前运行内部所有方法
        this.finallyArr = []   
        // 调用用户传入的函数,并将resolve与reject回传
        fn(resolve.bind(this), reject.bind(this))   
    }
})()

特别说明

这里跟大家强调说明一下,使用setTimeout不仅仅是单纯为了模仿异步效果,而是这样将调用next方法变为一个宏任务,可以确保在链式调用时,then方法之后进行执行。

这里将当传入的参数为promise的处理和finally方法的处理暂时去除了,等下面涉及到时再和大家单独说明。

Promise.prototype.then

该方法是整个Promise的核心,并且Promise A+规范规定的也是该方法,下面我们继续先上代码,我会把注释写全,再和大家讨论实现思路。

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let _self = this    // 记录前一个promise
    let promise = new MyPromise(function (resolve, reject) {
        // 将方法保存下来 在上一个的next中进行触发
        _self.next = () => {
            // 上一个promise的状态 决定调用哪个函数方法
            // 是成功的 则调用 onFulfilled
            if (_self['[[PromiseStatus]]'] == 'fulfilled') { 
                // 如果onFulfilled不存在 则将上一个的值直接传递给当前全新的promise
                if (typeof onFulfilled !== 'function') {
                    resolve.call(this, _self['[[PromiseValue]]'])
                }
                // 当前promise的状态由 执行的方法决定
                try {
                    // 将上一个的值进行回传 
                    let data = onFulfilled(_self['[[PromiseValue]]']) 
                    resolve.call(this, data)   // 成功 则调用 resolve方法
                } catch (error) {
                    reject.call(this, error)   // 失败则调用 reject
                }
            }
            // 是失败的 则调用 onRejected
            else if (_self['[[PromiseStatus]]'] == 'rejected') { 
                 // 如果没有错误处理方法则直接报错 当前的值
                if (typeof onRejected !== 'function') {
                    reject.call(this, _self['[[PromiseValue]]'])
                } 
                // 当前promise的状态由 执行的方法决定
                try {
                    let data = onRejected(_self['[[PromiseValue]]'])
                    resolve.call(this, data)   // 成功 则调用 resolve方法
                } catch (error) {
                    reject.call(this, error)   // 失败则调用 reject
                }
            }
            // 如果是等待状态 就空
        }
    })
    // 返回一个全新的promise
    return promise
}
}

上面的代码充斥着高阶函数和call、apply,可能不太好理解,下面和大家一起探讨一下 首先思路和大错特错那一章节中是一样的,即:

当前Promise的执行resolve、reject是由上一个Promise决定的

当前Promise的状态,是由该Promise的resolve、reject决定的

特别说明

我们在新建的Promise中并未去触发改变该状态的函数,而是将其保存在了上一个Promise的next属性中,当上一个Promise触发resolve方法时再去触发,这样就完成了异步的状态改变。

下面拿一个示例说说整个Promise发生的流程:

new MyPromise(function (resolve) {
    setTimeout(() => {
        resolve(42)
    }, 1000);
})
.then(res =>{
    console.log(res)
})

第一步:创建一个新的Promise,将一个匿名方法传入,将会在构造函数中被调用,等待一秒后会去触发resolve方法

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

第二步:调用首个Promise的then方法,创建一个全新的等待状态的Promise2,请将改变其状态的方法存在Promise1next属性中

...
let promise = new MyPromise(function (resolve, reject) {
    _self.next = () => {
        ...
    }
})
return promise

第三步:第一步的匿名函数setTimeout触发resolve方法,改变了首个Promise的状态和值,同时会去检查是否存在下一个Promise,如果存在则执行

...
if (this.next) {
    this.next()
    delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
}
...

第四步:next方法带着首个Promise的状态来了,改变Promise2的状态

...
// 上一个promise的状态 决定调用哪个函数方法
// 是成功的 则调用 onFulfilled
if (_self['[[PromiseStatus]]'] == 'fulfilled') {      
    // 如果onFulfilled不存在 则将现有的值传递给下一个全新的promise
    if (typeof onFulfilled !== 'function'){
         resolve.call(this, _self['[[PromiseValue]]'])
    }
    // 当前promise的状态由 执行的方法决定
    // 将上一个的值进行回传 
    try {
        let data = onFulfilled(_self['[[PromiseValue]]'])   
        resolve.call(this, data)   // 成功 则调用 resolve方法
    } catch (error) {
        reject.call(this, error)   // 失败则调用 reject
    }
}
...

第五步: Promise2resolvereject再次触发next方法,以此往复


我们再来看看这两句代码:

...
if (typeof onFulfilled !== 'function') {
    resolve.call(this, _self['[[PromiseValue]]'])
}
...
if (typeof onRejected !== 'function') {
    reject.call(this, _self['[[PromiseValue]]'])
}
...

但凡遇到空的then方法,将会直接将上一个Promise的状态和值复制,能够继续向下传递,就像这样子:

new MyPromise(function (resolve) {
    resolve(42)
})
.then()
.then()
.then(res =>{
    console.log(res)    // 42
})

对于空的Promise.then()根本无需理会,同样返回一个全新的空Promise即可

我们再回过头将reslove方法进行扩充,如果传入的值是Promise对象呢?我们将等待其完成然后再进行赋值操作。

...
// 如果res 是一个promise对象 则等待其值然后返回
if (res instanceof MyPromise) {
    res.then(val => {
        this['[[PromiseStatus]]'] = 'fulfilled'
        this['[[PromiseValue]]'] = val
    }, err => {
        this['[[PromiseStatus]]'] = 'rejected'
        this['[[PromiseValue]]'] = err
    })
} 
else {
    this['[[PromiseStatus]]'] = 'fulfilled'
    this['[[PromiseValue]]'] = res
}
...

then方法这是整个Promise实现最难的地方,大家要是没理解的话可以自己动手跑一下,我会将完整实现全部贴在最后。

Promise.prototype.catch

实现了then方法后,你会发现其实catch方法只是then方法的阉割版,即没有了成功的回调方法,只有失败的方法。实现代码很简单:

MyPromise.prototype.catch = function (onRejected) {
    // 直接调用then方法 并返回then返回的 promise
    return MyPromise.prototype.then.call(this, null, onRejected)
}

没错,直接调then方法即可

Promise.prototype.finally

该方法定义了无论Promise失败与否,都将会执行的函数。之前实现的thencatch方法都是和Promise的状态与值挂钩的, 而finally方法不是,只是单纯的在一个Promise调用结束后触发的方法,甚至它连回调参数也没有。所以我们重新设置一个对象参数来存储finally的参数。

return function (fn) {
    // 保存当前promise状态
    this['[[PromiseStatus]]'] = 'pending'   
    // 保存当前promise的值
    this['[[PromiseValue]]'] = undefined    
    // 指向下一个pomise
    this.next = null          
    // 保存所有的finally方法 当改promise当用完前运行内部所有方法
=>  this.finallyArr = []  
    // 调用用户传入的函数,并将resolve与reject回传
    fn(resolve.bind(this), reject.bind(this))  
}

finally方法中进行添加

MyPromise.prototype.finally = function (onFinally) {
    // 链式调用 但是不会返回新的promise对象
    this.finallyArr.push(onFinally)
    return this
}

同时注意,finally返回的是自身,与then、catch是不同的。 现在方法都存储起来了,只要在该Promise执行后调用就可以了,resolve函数添加代码如下:

...
setTimeout(() => {
    // 遍历finishArr并链式执行
    // 遍历执行finally 方法
    while (this.finallyArr.length > 0) {
        this.finallyArr.shift()()
    }
    delete this.finallyArr
    if (this.next) {
        this.next()
        delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
    }
}, 0)
...

并且当全部执行后就把该属性删除,这样在浏览器中打印出来就和原生的一模一样了,哈哈哈哈哈哈哈。

Promise.resolve

它返回一个成功状态的Promise,虽然我感觉它和我在闭包内实现的resolve方法差不多,但是我没有想到一个很好的办法来进行整合,如果有小伙伴想到的话请第一时间告知我!~ 代码实现很简单。

MyPromise.resolve = function (value) {
    // 如果是promise类型直接返回
    if (value instanceof MyPromise) return value
    return new MyPromise(function (resolve) {
        resolve(value)
    })
}

Promise.reject

它与MyPromise.resolve是一样的道理。

MyPromise.reject = function (value) {
    // 如果是promise类型直接返回
    if (value instanceof MyPromise) return value
    return new MyPromise(function (resolve, reject) {
        reject(value)
    })
}

MyPromise.race

该方法的定义是但凡接收到的类数组参数中有一个参数成功了就直接返回包含该参数的Promise,失败同理,直接上代码:

MyPromise.race = function (arr) {
    let promise = new MyPromise(function (resolve, reject) {
        for (let i = 0; i < arr.length; i++) {
            //  如果是promise 则使用then 进行监听 谁先完成或者失败就直接改变状态
            if (arr[i] instanceof MyPromise) {
                arr[i].then(function (res) {
                    resolve(res)
                }, function (err) {
                    reject(err)
                })
            } else {
                 // 如果不是promise 则直接返回结果
                resolve(arr[i])
            }
        }
    })
    return promise
}

在现在的Promise实现下,去异步监听值得变化变的异常简单,从而能够判断谁是第一个完成或失败的,从而返回它的状态。

Promise.all

该方法与race相反,所以类数组中的参数成功了才会返回一个包含它们结果的Promise,失败则直接返回错误消息。实现起来相较race稍难一点,需要判断当前类数组中的参数是否全部完成,但是在then方法下都信手捏来。

MyPromise.all = function (arr) {
    let promise = new MyPromise(function (resolve, reject) {
        let result = [] // 将结果保存
        let start = 0,  // 开始异步的数量 
            finish = 0      // 完成异步的数量
        for (let i = 0; i < arr.length; i++) {
            // 如果是promise 则使用then 进行监听
            if (arr[i] instanceof MyPromise) {
                ++start // 记录当前开始了多少个promise
                // 使用then就会被挂起监听
                arr[i].then(res => {
                    ++finish    // 记录当前完成了多少个promise
                    result[i] = res
                    // 全部完成则返回成功状态与数据
                    if (start == finish) resolve(result) 
                }, err => {
                    reject(err)     // 错了就直接返回
                })
            } else {
                result[i] = arr[i]
            }
        }
        // 如果没有异步方法 则直接返回结果
        if (!start) resolve(result)
    })
    return promise
}

通过在then方法中对startfinish的对比,从而能够知道当前完成的Promise是否是最后一个,进而返回全部结果。

完整实现代码

到这里就将整个Promise实现啦,同时我个人也使用promises-aplus-tests进行了测试,符合全部规范,大家可以安心食用。

let MyPromise = (function () {
    function resolve(res) {
        if (this['[[PromiseStatus]]'] != 'pending') return  // 如果存在状态则直接返回
        // 如果接收到的值是自己本身则报错
        if (res === this) {
            throw new Erroe('TypeError: Chaining cycle detected for promise #<Promise>')
        }
        // 如果res 是一个promise对象 则等待其值然后返回
        if (res instanceof MyPromise) {
            res.then(val => {
                this['[[PromiseStatus]]'] = 'fulfilled'
                this['[[PromiseValue]]'] = val
            }, err => {
                this['[[PromiseStatus]]'] = 'rejected'
                this['[[PromiseValue]]'] = err
            })
        } else {
            this['[[PromiseStatus]]'] = 'fulfilled'
            this['[[PromiseValue]]'] = res
        }

        // promise 是一个宏任务  使用 setTimeout 进行模拟
        setTimeout(() => {
            // 遍历finishArr并链式执行
            // 遍历执行finally 方法
            while (this.finallyArr.length > 0) {
                this.finallyArr.shift()()
            }
            delete this.finallyArr
            if (this.next) {
                this.next()
                delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
            }
        }, 0)
    }

    function reject(res) {
        if (this['[[PromiseStatus]]'] != 'pending') return  // 如果存在状态则直接返回
        this['[[PromiseStatus]]'] = 'rejected'
        this['[[PromiseValue]]'] = res
        setTimeout(() => {
            // 遍历执行finally 方法
            while (this.finallyArr.length > 0) {
                this.finallyArr.shift()()
            }
            delete this.finallyArr
            // 如果有下一个promise则传值,如果没有则直接报错
            if (this.next) {
                this.next()
                delete this.next     // 解除关联关系 防止无法被垃圾回收器回收
            } else {
                throw new Error(res)
            }
        }, 0)
    }
    return function (fn) {
        this['[[PromiseStatus]]'] = 'pending'
        this['[[PromiseValue]]'] = undefined
        this.next = null
        this.finallyArr = []   // 保存所有的finally方法 当改promise当用完前运行内部所有方法
        fn(resolve.bind(this), reject.bind(this))
    }
})()

MyPromise.resolve = function (value) {
    // 如果是promise类型直接返回
    if (value instanceof MyPromise) return value
    return new MyPromise(function (resolve) {
        resolve(value)
    })
}

MyPromise.reject = function (value) {
    // 如果是promise类型直接返回
    if (value instanceof MyPromise) return value
    return new MyPromise(function (resolve, reject) {
        reject(value)
    })
}

MyPromise.all = function (arr) {
    let promise = new MyPromise(function (resolve, reject) {
        let result = [] // 将结果保存
        let start = 0,  // 开始异步数量 
            finish = 0      // 完成异步的数量
        for (let i = 0; i < arr.length; i++) {
            // 如果是promise 则使用then 进行监听
            if (arr[i] instanceof MyPromise) {
                ++start // 记录当前开始了多少个promise
                // 使用then就会被挂起监听
                arr[i].then(res => {
                    ++finish    // 记录当前完成了多少个promise
                    result[i] = res
                    if (start == finish) resolve(result) // 全部完成则返回成功状态与数据
                }, err => {
                    reject(err)     // 错了就直接返回
                })
            } else {
                result[i] = arr[i]
            }
        }
        // 如果没有异步方法 则直接返回结果
        if (!start) resolve(result)
    })
    return promise
}

MyPromise.race = function (arr) {
    let promise = new MyPromise(function (resolve, reject) {
        for (let i = 0; i < arr.length; i++) {
            //  如果是promise 则使用then 进行监听 谁先完成或者失败就直接改变状态
            if (arr[i] instanceof MyPromise) {
                arr[i].then(function (res) {
                    resolve(res)
                }, function (err) {
                    reject(err)
                })
            } else {
                 // 如果不是promise 则直接返回结果
                resolve(arr[i])
            }
        }
    })
    return promise
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let _self = this    // 记录前一个promise
    let promise = new MyPromise(function (resolve, reject) {
        // 将方法保存下来 在上一个的next中进行改变状态操作
        _self.next = () => {
            // 上一个promise的状态 决定调用哪个函数方法
            // 当前是成功的 则调用 onFulfilled
            if (_self['[[PromiseStatus]]'] == 'fulfilled') {      
                // 如果onFulfilled不存在 则将现有的值传递给下一个全新的promise
                if (typeof onFulfilled !== 'function') {
                    resolve.call(this, _self['[[PromiseValue]]'])
                }
                // 当前promise的状态由 执行的方法决定
                try {
                    let data = onFulfilled(_self['[[PromiseValue]]'])
                    resolve.call(this, data)   // 成功 则调用 resolve方法
                } catch (error) {
                    reject.call(this, error)   // 失败则调用 reject
                }
            }
            // 当前是失败的 则调用 onRejected
            else if (_self['[[PromiseStatus]]'] == 'rejected') {  
                // 如果没有错误处理方法则直接报错 当前的值
                if (typeof onRejected !== 'function') {
                    reject.call(this, _self['[[PromiseValue]]'])  
                }
                // 当前promise的状态由 执行的方法决定
                try {
                    let data = onRejected(_self['[[PromiseValue]]'])
                    resolve.call(this, data)   // 成功 则调用 resolve方法
                } catch (error) {
                    reject.call(this, error)   // 失败则调用 reject
                }
            }
            // 如果是等待状态 就空
        }
    })
    // 需要返回一个全新的promise 并且这个promise就是当前处理的这个
    return promise
}

MyPromise.prototype.catch = function (onRejected) {
    // 直接调用then方法 并返回then返回的 promise
    return MyPromise.prototype.then.call(this, null, onRejected)
}

MyPromise.prototype.finally = function (onFinally) {
    // 链式调用 但是不会返回新的promise对象
    this.finallyArr.push(onFinally)
    return this
}

Promise.deferred = Promise.defer = function () {
    var dfd = {}
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}

module.exports = Promise

Promise.deferred是给测试提供的接口,无视。

结语

我还记得那是一个晴朗的早晨,外面天气很好,我看到一个题目姚手撸一个Promise,我说这有什么难的,我来。 不曾想,实现加上写文章整整花费了一个礼拜的业余时间,着实有点难顶。在此厚脸皮的跟大家要个,你看成吗?

我做完以后去网上查阅了一下,大家好像都用数组对回调方法进行存储,如果我一开始也去看这一类的文章再自己动手,可能脑海里也会习惯性的用已知方法解决。其实我想说就是,解决问题有时候不必一味的去模仿、照搬,静下来自己想一想,也许你会有你自己的答案。

原文链接:juejin.im

上一篇:这个惊呆了群众的CSS,必须学会!
下一篇:Jest前端自动化测试入门

相关推荐

  • 🔥 Promise|async|Generator 实现&amp;原理大解析 | 9k字

    笔者刚接触async/await时,就被其暂停执行的特性吸引了,心想在没有原生API支持的情况下,await居然能挂起当前方法,实现暂停执行,我感到十分好奇。好奇心驱使我一层一层剥开有关JS异步编程的...

    5 个月前
  • 面试题1:Promise递归实现拉取数据

    题目:请用promise递归实现拉取100条数据,每次拉取20条,结束条件为当次拉取不足20条或者已经拉取100条数据 ...

    3 个月前
  • 面试官要求我们手动实现 Promise.all

    情景: 最近面试,有两次被问到手动实现 Promise.all,不幸的是我都没把这题做好。因为我没有去准备这个,我不知道手动实现已有的 API 有什么意义。 但是为了防止以后还会遇到此类题,还是记录下...

    2 个月前
  • 阅读Promise A+规范

    本文主要是PromiseA规范(https://promisesaplus.com/)的翻译加上个人的理解。 1 什么是Promise A promise represents the eve...

    2 年前
  • 通过koa2和Promise.race()构造一个超时取消的ajax。

    MDN上说: 你可以使用AbortController.AbortController()构造函数创建一个新的AbortController对象。 使用AbortSignal 对象完成与DOM请求...

    2 年前
  • 通过Iterator控制Promise.all的并发数

    背景 异步是 js 一个非常重要的特性,但很多时候,我们不仅仅想让一系列任务并行执行,还想要控制同时执行的并发数,尤其是在针对操作有限资源的异步任务,比如文件句柄,网络端口等等。 看一个例子。

    9 个月前
  • 这样理解 promise

    官网解释 promise 表示一个异步操作的最终结果。 翻译 ==可以将promise理解为一个状态机==,它存在三种不同的状态,并在某一时刻只能有一种状态 pending 表示还在执行 ...

    1 年前
  • 这几个Promise的输出到底是?

    看一下以下几个单选题,正确的输出是什么呢? (1) A. print message once B. print message twice C. Unhandled Promise...

    6 个月前
  • 轻松实现链式调用Then, Catch的简易Promise

    轻松实现链式调用Then, Catch的简易Promise 再过不久,我也要投身到求职的浪潮之中,一直听说今年行情太差,自己所在的公司也就我一个前端, 一年经验还没人带,实在太难受。

    22 天前
  • 轻松上手promise原理(2):then的简单实现

    轻松上手promise原理(2):then的简单实现 在上一篇文章中,我们对于promise有了初步的认识,我们了解到promise是new出来的对象,有三个对应pedding,fulfilled,r...

    1 个月前

官方社区

扫码加入 JavaScript 社区