Handwritten promise Source

promise to introduce myself

promise: "a gentleman as good as gold, things will go the implementation of commitments"

promise of usage scenarios
  • Use promise an effective solution to the problem js asynchronous callback hell
  • Business logic can be separated from the data processing code more elegant, easy to read, more conducive to code maintenance
Basic usage of promise
function promiseTest() {
    let promise = new Promise((resolve, reject) => {
        let r = parseInt(Math.random() * 10)
        if (r % 2 == 0) {
            resolve('成功')
        } else {
            reject('失败')
        }
    })
    return promise
}
const promise = promiseTest()
promise.then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})
First to analyze the specifications of promise
  • promise has three states: pending, fulfilled, rejected. Method represents a pending waiting state, in this state, may perform resolve (), may also be performed Reject () method, fulfilld equal success state, this state execution resolve () method, Rejected for failure state, execution in this state reject () method, if successful will not be a failure, and vice versa
  • Each promsie then there is a method
  • If the new promise being given away will fail state (throw new Error ( 'error') will take the failed state)
// 手写promise源码
// 第一步:基础代码
class Mypromise {
    constructor(executor) {
        this.state = 'pending'  //状态值
        this.value = undefined  //成功的返回值
        this.reason = undefined //失败的返回值
        // 成功
        let resolve = (value) => {
            if (this.state == 'pending') {
                this.state = 'fullFilled'
                this.value = value
            }
        }
        // 失败
        let reject = (reason) => {
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
            }
        }
        try {
            // 执行函数
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    then(onFullFilled, onRejected) {
        // 状态为fulfuilled,执行onFullFilled,传入成功的值
        if (this.state == 'fullFilled') {
            onFullFilled(this.value)
        }
        // 状态为rejected,执行onRejected,传入失败的值
        if (this.state == 'rejected') {
            onRejected(this.reason)
        }
    }
}

const p = new Mypromise((resolve, reject) => {
    // resolve('success')   // 走了成功就不会走失败了
    throw new Error('失败') // 失败了就走resolve
    reject('failed')       // 走了失败就不会走成功
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})

At this time, a first layer of nine even completed yangshengong

But when encounter asynchronous calls, the code above will be stuck in a pending state, into the early magic, but also to continue to practice down, if it can not continue to break, it can not rise to the second tier state

When the following call will be stuck, unable to perform

const p = new Mypromise((resolve, reject) => {
    setTimeout(function() {
        resolve('success')
    }, 1000)
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})

In this case we use a publish-subscribe model who, at the time pending state will function successfully function and failure to keep their callback queue array, and so once reject or resolve, you call them:

When all the pending state to state in the successful implementation of the method are saved to an array onResolveCallbacks

When the state changes, the implementation of their release

Here is the complete code

class Mypromise {
    constructor(executor) {
        this.status = 'pending'  //状态值
        this.value = undefined   //成功的返回值
        this.reason = undefined  //失败的返回值
        this.onResolvedCallbacks = [] //成功的回调函数
        this.onRejectedCallbacks = [] //失败的回调函数
        // 成功
        let resolve = (value) => {
            // pending用来屏蔽的,resolve和reject只能调用一个,不能同时调用,这就是pending的作用
            if (this.status == 'pending') {
                this.status = 'fullFilled'
                this.value = value
                // 发布执行函数
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败
        let reject = (reason) => {
            if (this.status == 'pending') {
                this.status = 'rejected'
                this.reason = reason
                //失败执行函数
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            // 执行函数
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    then(onFullFilled, onRejected) {
        // 同步
        if (this.status == 'fullFilled') {
            onFullFilled(this.value)
        }
        if (this.status == 'rejected') {
            onRejected(this.reason)
        }
        // 异步
        if (this.status == 'pending') {
            // 在pending状态的时候先订阅
            this.onResolvedCallbacks.push(() => {
                // todo
                onFullFilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                // todo
                onRejected(this.reason)
            })
        }
    }
}

const p = new Mypromise((resolve, reject) => {
    setTimeout(function() {
        // resolve('success') // 异步调用的时候,this.status一直是pending状态,不会执行代码了,因此要改装成发布订阅者模式
        reject('failed')
    }, 1000)
    // resolve('success') // 走了成功就不会走失败了
    // throw new Error('失败') // 失败了也会走resolve
    // reject('failed')
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})

Congratulations teenager, the second layer of nine positive magic you have learned

Next continue learning magic Layer promise of chained calls

To achieve chained calls we have to adopt "the old monk to monk story" recursive Dafa, it can be said that the Foolish Old Man Dafa, many students may have been a little afraid of recursive functions is actually very simple, think about a child heard the old monk telling stories, as well as the Foolish old Man story became clear what is a recursive algorithm.

Let's take a look back at these two classic story:

"The old monk to monk a story: There was once a mountain, the mountain there was a temple, the temple has an old monk, the old monk to monk telling stories, once a mountain, the mountain there was a temple ......"

Yu Gong said: "Although I am of the dead, has a son Cunyan; son and grandchildren, Sun and children; child and the Son, there are sub-Sun; infinite deficient children and grandchildren also, but Hill does not improve, why not flat "

Recursion is not very simple?

Do not understand the norms of the students first take a look Promises / A + specification documents:

English specification documents: https://promisesaplus.com/

The following first to analyze the situation of the use of chained calls, and then there may arise

const p = new Promise((resolve, reject) => {
    resolve(100)
})
p.then((data) => {
    return 100 * data
}, (err) => {
    console.log(err)
}).then((data) => {
    return new Promise((resolve, reject) => {
        console.log(data)
        resolve(data)
    })
}).then((data) => {
    console.log('result', data) // 10000
})

According to the use of native promise then, we summarize:

1.then method if it returns a normal value, this will be our common values ​​passed to the next then

2.then method if a promise to return the object, we will object to perform this promise returns the results to the next then

Common values ​​are passed to do well, then we will be the first of onFulfilled function returns a value to keep inside x variable, and then resolve to go out on it

then(onFullFilled, onRejected) {
        // 这样就是一个递归
        let promise2 = new Mypromise((resolve, reject) => {
            // 函数里面调函数就跟第一次使用一样,主要的是这里面的this指向怎么变化的
            // 同步
            let x
            console.log('this', this)
            if (this.status == 'fullFilled') {
                // 箭头函数,无论this一直是指向最外层的对象
                x = onFullFilled(this.value)
                resolve(x) // resolve(x) // 这一步x只能处理普通值,但是x可能是一个函数对象,或者promise,所以要对x进行判断
                // 添加一个resolvePromise()的方法来判断x跟promise2的状态,决定promise2是走成功还是失败
            }
            if (this.status == 'rejected') {
                x = onRejected(this.reason)
                reject(x)
            }
            // 异步
            if (this.status == 'pending') {
                // 在pending状态的时候先订阅
                this.onResolvedCallbacks.push(() => {
                    // todo
                    x = onFullFilled(this.value)
                    resolve(x)
                })
                this.onRejectedCallbacks.push(() => {
                    // todo
                    x = onRejected(this.reason)
                    resolve(x)
                })
            }
        })
        return promise2   //then方法返回一个promise对象
    }

The complex is then returned inside is a promise of how to do, because we want to return to the promise of his execution state judge to decide whether to take the success of state, or failed state, this time we will write a function resolvePromise judgment (promise2, x, resolve, reject) to complete the determination

then(onFullFilled, onRejected) {
        // 这样就是一个递归
        let promise2 = new Mypromise((resolve, reject) => {
            // 箭头函数,无论this一直是指向最外层的对象
            // 同步
            let x
            if (this.status == 'fullFilled') {
                setTimeout(() => {
                    try {
                        x = onFullFilled(this.value)
                        // 添加一个resolvePromise()的方法来判断x跟promise2的状态,决定promise2是走成功还是失败
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                // MDN 0>=4ms
            }
            if (this.status == 'rejected') {
                setTimeout(() => {
                    try {
                        x = onRejected(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
            }
            // 异步
            if (this.status == 'pending') {
                // 在pending状态的时候先订阅
                this.onResolvedCallbacks.push(() => {
                    // todo
                    setTimeout(() => {
                        try {
                            x = onFullFilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                })
                this.onRejectedCallbacks.push(() => {
                    // todo
                    setTimeout(() => {
                        try {
                            x = onRejected(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                })
            }
        })
        return promise2
    }

Here again achieve the core method of resolvePromise

The main effect of this method is used to determine the value of x, if x is a normal value, the return value of x directly, if the value of x is a Promise, will return to x.then () execution result, The core code is as follows

const resolvePromise = (promise2, x, resolve, reject) => {
    // x和promise2不能是同一个人,如果是同一个人就报错
    if (promise2 === x) {
        return reject(
            new TypeError('Chaining cycle detected for promise #<promise>')
        )
    }
    // 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
    if (typeof x === 'object' && x != null || typeof x === 'function') {
        try {
            let then = x.then // 取then可以报错,报错就走reject()
            if (typeof then === 'function') {
                // 用then.call()为了避免在使用一次x.then报错
                then.call(x, y => {
                    console.log('y', y)
                    resolve(y)// 采用promise的成功结果,并且向下传递
                }, r => {
                    reject(r)// 采用promise的失败结果,并且向下传递
                })
            } else {
                resolve(x)// x不是一个函数,是一个对象
            }
        } catch (err) {
            reject(err)
        }
    } else {
        // x是一个普通值
        resolve(x)
    }
}

Details of the place to see comments

At this time, the case of basic conditions have been almost achieved, one of the following further below, the value of x which comprises promise

const p2 = p.then((data) => {
    return new Mypromise((resolve, reject) => {
        resolve(new Mypromise((resolve, reject) => {
            setTimeout(() => {
                resolve(data * 1000)
            }, 1000)
        }))// 这里很可能又是一个promise函数
    })
})

We only need to call a callback to determine the value of x in time, you can solve the above problems

Here is the complete source code:

const isFunction = (value) => typeof value === 'function'
const PENDING = 'pending'
const RESOLVED = 'fulFilled'
const REJECTED = 'rejected'
const resolvePromise = (promise2, x, resolve, reject) => {
    // x和promise2不能是同一个人,如果是同一个人就报错
    // 加一个开关,防止多次调用失败和成功,跟pending状态值一样的逻辑一样,走了失败就不能走成功了,走了成功一定不能在走失败
    if (promise2 === x) {
        return reject(
            new TypeError('Chaining cycle detected for promise #<promise>')
        )
    }
    // 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        let called
        try { // 预防取.then的时候错误
            let then = x.then // Object.definePropertype
            if (typeof then === 'function') {
                // 用then.call()为了避免在使用一次x.then报错
                then.call(x, y => {
                    // resolve(y)// 采用promise的成功结果,并且向下传递
                    if (called) {
                        return
                    }
                    called = true
                    // y有可能是一个promise,那么我们就要继续使用回调函数,直到解析出来的值是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) {
                        return
                    }
                    called = true
                    reject(r)// 采用promise的失败结果,并且向下传递
                })
            } else {
                if (called) {
                    return
                }
                called = true
                resolve(x)// x不是一个函数,是一个对象
            }
        } catch (err) {
            if (called) {
                return
            }
            called = true
            reject(err)
        }
    } else {
        // x是一个普通值
        resolve(x)
    }
}
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        // 成功
        let resolve = (value) => {
            // pending最屏蔽的,resolve和reject只能调用一个,不能同时调用,这就是pending的作用
            if (this.status == PENDING) {
                this.status = RESOLVED
                this.value = value
                // 发布执行函数
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败
        let reject = (reason) => {
            if (this.status == PENDING) {
                this.status = REJECTED
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {
            // 执行函数
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    then(onFulFilled, onRejected) {
        // onfulfilled, onrejected 都是可选参数
        onFulFilled = isFunction(onFulFilled) ? onFulFilled : data => data
        onRejected = isFunction(onRejected) ? onRejected : err => {
            throw err
        }
        let promise2 = new MyPromise((resolve, reject) => {
            // 箭头函数,无论this一直是指向最外层的对象
            // 同步
            if (this.status == RESOLVED) {
                setTimeout(() => {
                    try {
                        let x = onFulFilled(this.value)
                        // 添加一个resolvePromise()的方法来判断x跟promise2的状态,决定promise2是走成功还是失败
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                // MDN 0>=4ms
            }
            if (this.status == REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) { // 中间任何一个环节报错都要走reject()
                        reject(err)
                    }
                }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
            }
            // 异步
            if (this.status == PENDING) {
                // 在pending状态的时候先订阅
                this.onResolvedCallbacks.push(() => {
                    // todo
                    setTimeout(() => {
                        try {
                            let x = onFulFilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                })
                this.onRejectedCallbacks.push(() => {
                    // todo
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) { // 中间任何一个环节报错都要走reject()
                            reject(err)
                        }
                    }, 0) // 同步无法使用promise2,所以借用setiTimeout异步的方式
                })
            }
        })
        return promise2
    }
}

The core of this code is finished, and we use promises-aplus-tests to detect what plug-ins

Installation: npm install promises-aplua-tests -g plug

Add the following code:

MyPromise.defer = MyPromise.deferred = function() {
    let dfd = {}
    dfd.promise = new MyPromise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
module.exports = MyPromise

Run: promises-aplus-tests promise.js

ok, perfect

The core of this code has been achieved, static methods behind, catch, all, race, resolve, reject, finally is to small scale chopper, and will not repeat, just paste the code

catch method:
// catch方法
MyPromise.prototype.catch = function(onReJected) {
    // 返回一个没有第一个参数的then方法
    return this.then(undefined, onReJected)
}
promise of all methods
// 写一个判断函数是否是一个promise的方法
const isPromise = (value) => {
    if ((value != null && typeof value === 'object') || typeof value === 'function') {
        if (typeof value.then == 'function') {
            return true
        }
    } else {
        return false
    }
}
// static all方法
MyPromise.all = (lists) => {
    // 返回一个promise
    return new MyPromise((resolve, reject) => {
        let resArr = [] // 存储处理的结果的数组
        // 判断每一项是否处理完了
        let index = 0
        function processData(i, data) {
            resArr[i] = data
            index += 1
            if (index == lists.length) {
                // 处理异步,要使用计数器,不能使用resArr==lists.length
                resolve(resArr)
            }
        }
        for (let i = 0; i < lists.length; i++) {
            if (isPromise(lists[i])) {
                lists[i].then((data) => {
                    processData(i, data)
                }, (err) => {
                    reject(err) // 只要有一个传入的promise没执行成功就走reject
                    return
                })
            } else {
                processData(i, lists[i])
            }
        }
    })
}
promise of race method
// promise的race方法
// 两个方法赛跑,哪个赢了就先返回哪个的状态
MyPromise.race = (lists) => {
    return new MyPromise((resolve, reject) => {
        for (let i = 0; i < lists.length; i++) {
            if (isPromise(lists[i])) {
                lists[i].then((data) => {
                    resolve(data)// 哪个先完成就返回哪一个的结果
                    return
                }, (err) => {
                    reject(err)
                    return
                })
            } else {
                resolve(lists[i])
            }
        }
    })
}
promise static methods resolve ()
// 静态resolve方法
MyPromise.resolve = (value) => {
    // 如果是一个promise对象就直接将这个对象返回
    if (isPromise(value)) {
        return value
    } else {
        // 如果是一个普通值就将这个值包装成一个promise对象之后返回
        return new MyPromise((resolve, reject) => {
            resolve(value)
        })
    }
}
The promise Reject () method
// 静态reject方法
MyPromise.reject = (value) => {
    return new MyPromise((resolve, reject) => {
        reject(value)
    })
}
promise of finally method
// 终极方法finally finally其实就是一个promise的then方法的别名,在执行then方法之前,先处理callback函数
MyPromise.prototype.finally = function(cb) {
    return this.then(
        value => MyPromise.resolve(cb()).then(() => value)
        ,
        reason => MyPromise.reject(cb()).then(() => { throw reason })
    )
}

Boy, congratulations promise has been practicing martial art Dacheng, is not that he is very fast hardware, you can walk down the mountain rivers and lakes, Robin Hood, shovel the evil forces, Moji, promise into practice Dafa, has only just begun, still ahead many Dafa waiting How about you, there are many strange waiting for you to finish, upgrade, can not learn, do not go down to think, no matter what time should remember that the mountains there, Behind the technical sophistication moment can not neglect.

Guess you like

Origin www.cnblogs.com/xinggood/p/11836096.html