如何手写一个简单的Promise函数

248 阅读3分钟

我们先用Promise实现一个简单的功能,输入一个数字n,n秒后打印这个n,代码如下:

let logn = function(n) {
    return new Promise((resolve, reject) => {
    	setTimeout(() => {
            resolve(n)
        }, n * 1000)
    })
}
logn(1).then(res => {
    console.log(res)
})

分析下代码结构,不难发现:

1:Promise是一个构造函数,它接收一个函数fn作为入参
2:fn有两个参数:回调函数resolve、回调函数reject,resolve接收成功后的数据
3:Promise的实例有个then方法,这个方法接收一个回调函数cb

我们初步判断Promie函数大概结构如下:

// MyPromise是构造函数,所以要大写开头
let MyPromise = function(fn) {// 接收一个fn作为参数
    // 定义一下fn的回调函数resolve,resolve接收success信息
    let resolve = function(res) {
        
    }
    // 定义一下fn的回调函数reject,reject接收err信息
    let reject = function(err) {
        
    }
    // 给实例添加一个then方法,且这个方法接收一个回调函数cb
    this.then = function(cb) {
        
    }
}

接下来分析下几个函数是怎么跑的,经过仔细分析得出如下结论:

// 5个函数执行顺序如下
1new Promise(fn)  // 实例化logn
2fn(resolve, reject)  // 设置setTimeout
3then(cb)  // 接收成功回调cb
4resolve(res) or reject(err)  // 调用cb

更新一下MyPromise:

let MyPromise = function(fn) {
    // 1、先实例化logn
    let resolve = function(res) {
        // 4、执行成功的回调函数cb,并把res传递进去
        this.success(res)
    }
    let reject = function(err) {
        
    }
    this.then = function(cb) {
        // 3、接收成功的回调函数cb
        this.success = cb
    }
    // 2、执行fn,设置setTimeout
    fn(resolve, reject)
}

我们把Promise换成MyPromise试一下:

let logn = function(n) {
    return new MyPromise((resolve, reject) => {
    	setTimeout(() => {
            resolve(n)
        }, n * 1000)
    })
}
logn(1).then(res => {
    console.log(res)
})

执行代码发现控制台报错了:this.success is not a function。因为resolve是定时器中的函数,this默认指向window。所以修改下代码,给resolve函数bind一下this,并执行下MyPromise,发现1秒后打印了1

let MyPromise = function(fn) {
    let resolve = function(res) {
        this.success(res)
    }
    let reject = function(err) {
        
    }
    this.then = function(cb) {
        this.success = cb
    }
    fn(resolve.bind(this), reject.bind(this))
}
let logn = function(n) {
    return new MyPromise((resolve, reject) => {
    	setTimeout(() => {
            resolve(n)
        }, n * 1000)
    })
}
logn(1).then(res => {
    console.log(res)
})

我们发现MyPromise只处理了resolve函数,没有处理reject函数。为了测试reject,所以我们改变一下需求,传一个0 - 10之间的随机整数n,如果n小于5,1秒后打印'成功',否则打印'失败':

let MyPromise = function(fn) {
    let resolve = function(res) {
        this.success(res)
    }
    // copy一下resolve
    let reject = function(err) {
        this.error(err)
    }
    this.then = function(cb) {
        this.success = cb
    }
    // copy一下this.then
    this.catch = function(cb) {
        this.error = cb
    }
    fn(resolve.bind(this), reject.bind(this))
}
let logn = function(n) {
    return new MyPromise((resolve, reject) => {
    	setTimeout(() => {
            if(n < 5) {
            	resolve('成功')
            }else{
            	reject('失败')
            }
        }, 1000)
    })
}
let n = (Math.random() * 10).toFixed(0)
logn(n).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

执行发现又报错了:Cannot read property 'catch' of undefined。因为then和catch都没有return数据,联系到函数的链式编程,我们加一个return this,再执行一下,发现成功实现了catch:

let MyPromise = function(fn) {
    let resolve = function(res) {
        this.success(res)
    }
    let reject = function(err) {
        this.error(err)
    }
    this.then = function(cb) {
        this.success = cb
        return this
    }
    this.catch = function(cb) {
        this.error = cb
        return this
    }
    fn(resolve.bind(this), reject.bind(this))
}
let logn = function(n) {
    return new MyPromise((resolve, reject) => {
    	setTimeout(() => {
            if(n < 5) {
            	resolve('成功')
            }else{
            	reject('失败')
            }
        }, 1000)
    })
}
let n = (Math.random() * 10).toFixed(0)
logn(n).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

以上就是我在写promise的时候遇到的问题和个人思考,欢迎小伙伴留言评论