Promise simple use

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* Asynchronous operation succeeded */){
    resolve(value);
  } else{ reject(error); } # # #})Copy the code

Basic terms and requirements for Promsie

The term

  1. A “promise” is an object or function with a then method that behaves according to this specification
  2. “Thenable” is the object or function that defines the then method. It can be an object or a function
  3. “Value” is any valid Javascript value (including undefined,thenable, promise)
  4. “Exception” is a value thrown using the throw statement
  5. “Reason” is the value of the rejected promise

requirements

1. Promsie state

  1. A promise must be in one of three states: pending, fulfilled, and rejected. This is a big pity.
  2. When a promise is in the pending state, it can be postponed or rejected
  3. This is a big pity. When a promise is in a fulfilled state, it cannot be changed into any other state. There must be a value, which cannot be changed
  4. When a promise is in the Rejected state, it cannot go into any other state. There must be a reason and this reason cannot be changed

2. Then method

A promise must provide then methods to access its current or final value or cause. Promise’s then method takes two arguments

promise.then(onFulfilled, onRejected)
Copy the code

2.1 Ondepressing and onRejected are both optional parameters:

This will be a pity if onFulfilled is not a function, which must be ignored. If onFulfilled is not a function, which must be ignored

2.2 If ondepressing is a function:

  1. This function must be called after the promise is fulfilled, with the promise’s value as its first argument
  2. This function must not be called until the promise is fulfilled
  3. This function must not be called more than once

2.3 If onRejected is a function

.

OnFulfilled and onRejected must not be called until the platform code is included only in the execution context.

2.5 onFulfilled and onRejected must be called as functions

OnFulfilled and onRejected must be called as functions (i.e. with no this value)

2.6 Then can be called multiple times in the same promise

  1. If/when the promise is fulfilled, each corresponding onFulfilled callback must be invoked according to the original then order
  2. If/when a promise is rejected, the corresponding onRejected callbacks must be called in the original then order

Preliminary source code:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) = > {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
            }
        }
        const reject = (reason) = > {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
            }
        }
        executor(resolve, reject)
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}
module.exports = MyPromise
Copy the code

Make a little improvement

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        const resolve = (value) = > {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
            }
        }
        const reject = (reason) = > {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
            }
        }
        try { / / changes to the point
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
    }
}
module.exports = MyPromise
Copy the code

Implementation of true asynchronous operation problems:

let promise = new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('Success')},2000)
})

promise.then((value) = > {
    console.log('FulFilled', value)
}, (reason) = > {
    console.log('Rejected', reason)
})
Copy the code

Solution: Publish and subscribe

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) = > {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                / / release
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
        const reject = (reason) = > {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                / / release
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        if(this.status === FULFILLED) {
            onFulfilled(this.value)
        }
        if(this.status === REJECTED) {
            onRejected(this.reason)
        }
        if(this.status === PENDING) {
            / / subscribe
            this.onFulfilledCallbacks.push(() = > {
                onFulfilled(this.value)
            })
            / / subscribe
            this.onRejectedCallbacks.push(() = > {
                oRejected(this.reason)
            })
        }
    }
}
Copy the code

Promise chain-call feature

Attribute 1: Returns a result from the next then callback

let promise1 = new Promise((resolve, reject) = > {
    resolve('success')
}).then((value) = > {
    return value
}).then((value) = > {
    console.log('value', value)
})
Copy the code

Feature 2: If the return is a promise, the next THEN returns the promsie return, not the promise

let promise1 = new Promise((resolve, reject) = > {
    resolve('success')
}).then((value) = > {
    return new Promise((resolve) = > {
        resolve('success2')
    })
}).then((value) = > {
    console.log('value', value)
})
Copy the code

Attribute 3: The previous THEN returns a promise, reject, and the next THEN goes onRejected

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    console.log('value', value)
}, (reason) = > {
    console.log('reason', reason)
})
Copy the code

Attribute 4: After the last THEN passes the failed callback function, the next THEN passes the successful callback function, and its value is the value returned by the last rejected callback function

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    console.log('value', value)
}, (reason) = > {
    console.log('reason', reason)
}).then((value) = > {
    console.log('value2', value)
})
Copy the code

Attribute 5: The callback of the previous THEN is abnormal, and the callback of the next THEN is failed

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    throw new Error('Error')},(reason) = > {
    throw new Error('Error')
}).then((value) = > {
    console.log('Error', value)
}, (reason) = > {
    console.log('reason2', reason)
})
Copy the code

Attribute 6: An exception occurs in the last THEN callback, and the next THEN does not define onRejected, but is followed by a. Catch callback, which executes a. Catch callback

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    throw new Error('Error')},(reason) = > {
    throw new Error('Error')
}).then((value) = > {
    console.log('Error', value)
}).catch((r) = > {
    console.log('r', r)
})
Copy the code

Feature 7: Then and catch will find the last failed callback function to execute

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    throw new Error('Error')},(reason) = > {
    throw new Error('Error')
}).then((value) = > {
    console.log('Error', value)
}, (r) = > {
    console.log('the onRejected r', r)
}).catch((r) = > {
    console.log('the catch of r', r)
})
Copy the code

Attribute 8: Catch continues after. Then executes the successful callback function

let promise1 = new Promise((resolve, reject) = > {
    reject('reason')
}).then((value) = > {
    throw new Error('Error')},(reason) = > {
    throw new Error('Error')
}).then((value) = > {
    console.log('Error', value)
}, (r) = > {
    console.log('the onRejected r', r)
}).catch((r) = > {
    console.log('the catch of r', r)
}).then((value) = > {
    console.log(This is a big onpity.,value)
}, (reason) = > {
    console.log('Catch then onRejected',reason)
})
Copy the code

conclusion

Conditions for successful execution of the callback function

  1. The previous then return returns a plain javascript value
  2. The previous then returns a new promise, and the final state of the promise is success, resolve(value), which is available in the callback function

Conditions for executing failed callback functions

  1. The previous then returns the new promise, and the final state of the new promise is reject(reason), which is available in the callback function
  2. The previous THEN threw an exception

Why does then end up making a chain call because it returns a new promise

Chain call source code

Then must return a promise

then (onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) = > {
            if(this.status === FULFILLED) {
                try { // Failure summary 2
                    let x = onFulfilled(this.value)
                    resolve(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    let x = onRejected(this.reason)
                    reject(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === PENDING) {
                / / subscribe
                this.onFulfilledCallbacks.push(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
                / / subscribe
                this.onRejectedCallbacks.push(() = > {
                    try {
                        let x = onRejected(this.reason)
                        reject(x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
Copy the code
  1. If onFulfilled or onRejected returns a value x, run the Promise Resolution
  2. If onFulfilled or onRejected throws an exception E,promise2 must be rejected and e will be the reason
  3. If onFulfilled is not a method, and promise1 has been fulfilled, fulfiled with the same value as promise1 must be fulfilled.
  4. If onRejected is not a method and promise1 has been rejected, promise2 must use the same reason as promise1 to reject (rejected)

Chain calls override the THEN method

X doesn’t have to be a normal value, it could be another promise

let promise1 = new MyPromise((resolve, reject) = > {
    resolve('success')
}).then((value) = > {
    return new MyPromise((resolve, reject) = > {
        resolve('success2')
    })
}).then((value) = > {
    console.log('value', value)
})
Copy the code

I’m going to evaluate what x is

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) = > {
            if(this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                / / release
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
        const reject = (reason) = > {
            if(this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                / / release
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }
    then (onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) = > {
            if(this.status === FULFILLED) {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                    resolve(x)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === REJECTED) {
                try {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }
            if(this.status === PENDING) {
                / / subscribe
                this.onFulfilledCallbacks.push(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                / / subscribe
                this.onRejectedCallbacks.push(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    resolve(x)
}
Copy the code

PromiseA+ specification: If the callback returns a promise, the current state of the promise now depends on the state of the promsie returned

Therefore, the promsie that is forbidden to return cannot be the same as the current promsie

There are two questions:

  1. Ondepressing must be called after the asynchronous operation is completed

  2. At this point, the instance of Promise2 has not been created yet

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) = > {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                / / release
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
        const reject = (reason) = > {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                / / release
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) = > {
            if (this.status === FULFILLED) {
                setTimeout(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                / / subscribe
                this.onFulfilledCallbacks.push(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                        resolve(x)
                    } catch (e) {
                        reject(e)
                    }
                })
                / / subscribe
                this.onRejectedCallbacks.push(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    resolve(x)
}
Copy the code

Implement resolvePromise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) = > {
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                / / release
                this.onFulfilledCallbacks.forEach(fn= > fn())
            }
        }
        const reject = (reason) = > {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                / / release
                this.onRejectedCallbacks.forEach(fn= > fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        let promise2 = new MyPromise((resolve, reject) = > {
            if (this.status === FULFILLED) {
                setTimeout(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                        console.log('onRejected ')
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                / / subscribe
                this.onFulfilledCallbacks.push(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                / / subscribe
                this.onRejectedCallbacks.push(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if(promise2 === x) {
        console.log(1)
        return reject(new TypeError('Chaining cycle detected for promise#<MyPromise>'))}if((typeof x === 'object'&& x ! = =null) | |typeof x === 'function') {
        try {
            let then = x.then // throw error
            if(typeof then === 'function') { // x is a Promise
                then.call(x, (y) = > {
                    resolve(y)
                }, (r) = > {
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            reject(e)
        }
    } else {
        resolve(x)
    }
}
Copy the code
  1. A PROMsie is returned and resolve or Reject is called multiple times
  2. The x callback is again a promise
  3. promise2.then().then().then().then
function resolvePromise(promise2, x, resolve, reject) {
    if(promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise#<MyPromise>'))}let called = false
    if((typeof x === 'object'&& x ! = =null) | |typeof x === 'function') {
        try {
            let then = x.then // throw error
            if(typeof then === 'function') { // x is a Promise
                then.call(x, (y) = > {
                    if(called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, (r) = > {
                    if(called) return
                    called = true
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            if(called) return
            called = true
            console.log('hahahha')
            reject(e)
        }
    } else {
        resolve(x)
    }
}

then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value
        onRejected = typeof onRejected === 'function' ? onRejected : reason= > { throw reason } 
        let promise2 = new MyPromise((resolve, reject) = > {
            if (this.status === FULFILLED) {
                setTimeout(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                / / subscribe
                this.onFulfilledCallbacks.push(() = > {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
                / / subscribe
                this.onRejectedCallbacks.push(() = > {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        })
        return promise2
    }

Copy the code