Basic Concepts of Promise

ES6 complies with the Promise A+ specification

Promise has three states:

  • Waiting for,pending)
  • Cash (fulfilled) also called solutionresolved
  • Refuse to (reject)

The state is from pending to depressing or reject. Once the state is set, it is irreversible

Promise’s then method takes two arguments

  • The callback function that onResolved
  • OnRejected rejects the callback function

The then method returns a promise and can be called by a promise multiple times

Promise static method

  • Promise. Resolve (reference 1)
  • Promise. Reject (reference 1)

Promise. Resolve (reference 1)

Parameter 1: resolve the value of this contract; Passing extra parameters will be ignored

This static method behaves like an empty wrapper if the argument is a contract ———— js elevation system will resolve the value for you

let p = Promise.resolve(7);
setTimeout(() = > {
    console.log(p === Promise.resolve(p), p)
}, 0);
setTimeout(() = > {
    console.log(p === Promise.resolve(Promise.resolve(p)), p)
}, 0);
Copy the code

See no

This method is idempotent and preserves the state of the input contract

let p = new Promise(() = >{});
setTimeout(() = > {
    console.log(p === Promise.resolve(p), p)
}, 0);
setTimeout(() = > {
    console.log(p === Promise.resolve(Promise.resolve(p)), p)
}, 0);
Copy the code

Note: This static method can wrap any non-term value, including an error object, and convert it to a term. So it can lead to behavior that doesn’t meet expectations

let p = Promise.resolve(new Error('err'));
setTimeout(() = > {
    console.log(p)
}, 0);
Copy the code

Promise. Reject (reference 1)

Instantiate a reject contract and throw an asynchronous error (this error cannot be caught by a try/catch, only by a reject handler)Copy the code

Parameter 1: the reason for the rejection period. This parameter is also passed to subsequent rejection handlers

let p = Promise.reject(3);
setTimeout(() = > {
    console.log(p)
}, 0);

p.then(null.e= > {
    setTimeout(() = > {
        console.log(p)
    }, 0);
})
Copy the code

Note that promise.reject () does not copy the idempotent logic of promise.resolve (); if it is passed a date, that date becomes the rejection handler it returns

console.log(Promise.reject(Promise.reject()))
Copy the code

Try /catch can catch only synchronous errors. Asynchronous errors, such as rejected contracts, cannot be caught

try{
    throw new Error('err')}catch (e) {
    console.log(e)
}

try{
    Promise.reject(new Error('p-err'))}catch (e) {
    console.log(e)
}
Copy the code

Instance method of Promise

then(onResolved, onRejected)

  • Arguments are two functions and can be accepted by the functionPromisebypendingParameters passed to the determined state
  • onResolvedThe cashing process
  • onRejectedRejection handler
function onResolved(id, data) {
    setTimeout(() = > {
        console.log("resolved", id, data)
    }, 0);
}
function onRejected(id, data) {
    setTimeout(() = > {
        console.log("rejected", id, data)
    }, 0);
}

let p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('resolve')},3000);
})

let p2 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        reject('reject')},3000);
})

p1.then((data) = > {
    onResolved('p1', data)
}, (data) = > {
    onRejected('p1', data)
})

p2.then((data) = > {
    onResolved('p2', data)
}, (data) = > {
    onRejected('p2', data)
})
Copy the code

After 3 s

The two processing arguments in then() are optional, and any non-functional arguments passed to then() are ignored. If parameter 1 does not want to pass, but parameter 2 needs to be passed, simply pass NULL at parameter 1

p1.then('dfsdfdssff') // Non-function handlers are ignored and not recommended
p2.then(null.() = > onRejected('p2')) // Do not pass the onResolved code
Copy the code
setTimeout(() = > {
    console.log(p1)
}, 0);
setTimeout(() = > {
    console.log(p2)
}, 0);
setTimeout(() = > {
    console.log(p1 === p2)
}, 0);
Copy the code

This new instance of P2 is built based on the onResolved handler return value. That is, the return value of the handler generates a new term using a promise.resolve () wrapper. If no handler is provided, promise.resolve () wraps the value of the previous term. If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined

Return value for then() – onResolved

let p1 = Promise.resolve('foo');

// then does not pass the handler
let p2 = p1.then();
setTimeout(() = > {
    console.log(p2)
}, 0);
Copy the code

If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined

let p3 = p1.then(() = > {
    return undefined
})
let p4 = p1.then(() = > {})
let p5 = p1.then(() = > Promise.resolve())
setTimeout(() = > {
    console.log(p3) //Promise {<fulfilled>: undefined}
    console.log(p4) //Promise {<fulfilled>: undefined}
    console.log(p5) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code

If there is a displayed return value, promise.resolve () wraps that value

let p6 = p1.then(() = > {
    return 'bar'
})
let p7 = p1.then(() = > Promise.resolve('bar'))
setTimeout(() = > {
    console.log(p6) //Promise {<fulfilled>: "bar"}
    console.log(p7) //Promise {<fulfilled>: "bar"}
}, 0);
Copy the code

Promise.resolve() preserves the returned term

let p8 = p1.then(() = > {
    return new Promise(() = >{})})let p9 = p1.then(() = > {
    return Promise.resolve()
})
setTimeout(() = > {
    console.log(p8) //Promise {<pending>}
    console.log(p9) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code

Throwing an exception returns the rejected contract

let p10 = p1.then(() = > {
    throw 'baz'
})
setTimeout(() = > {
    console.log(p10)
}, 0);
Copy the code

If an error value is returned, the rejection action as above is not triggered, but the error object is wrapped in a resolution contract

let p11 = p1.then(() = > {
    return Error('qux')})setTimeout(() = > {
    console.log (p11),,,Promise {<fulfilled>: undefined}},0);
Copy the code

The return value of then() – onRejected

The onRejected handler is also wrapped in promise.resolved (). The onRejected handler’s job is to catch asynchronous errors, so it returns a resolution date if it does not throw an exception after catching an error

let p1 = Promise.reject('foo');

// then does not pass the handler
let p2 = p1.then();
setTimeout(() = > {
    console.log(p2)
}, 0);
Copy the code

When an error is caught, instead of throwing an exception, the Promise. Resolved () wrapped date is returned

let p3 = p1.then(null.() = > {
    return undefined
})
let p4 = p1.then(null.() = > {})
let p5 = p1.then(null.() = > Promise.resolve())
setTimeout(() = > {
    console.log(p3) //Promise {<fulfilled>: undefined}
    console.log(p4) //Promise {<fulfilled>: undefined}
    console.log(p5) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code

If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined

let p6 = p1.then(null.() = > {
    return 'bar'
})
let p7 = p1.then(null.() = > Promise.resolve('bar'))
setTimeout(() = > {
    console.log(p6) //Promise {<fulfilled>: "bar"}
    console.log(p7) //Promise {<fulfilled>: "bar"}
}, 0);
Copy the code

If there is a displayed return value, promise.resolve () wraps that value

let p8 = p1.then(null.() = > {
    return new Promise(() = >{})})let p9 = p1.then(null.() = > {
    return Promise.resolve()
})
setTimeout(() = > {
    console.log(p8) //Promise {<pending>}
    console.log(p9) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code

Throwing an exception returns the rejected contract

let p10 = p1.then(null.() = > {
    throw 'baz'
})
setTimeout(() = > {
    console.log(p10)
}, 0);
Copy the code

If an error value is returned, the rejection action as above is not triggered, but the error object is wrapped in a resolution contract

let p11 = p1.then(null.() = > {
    return Error('qux')})setTimeout(() = > {
    console.log(p11)
}, 0);
Copy the code

Promise.prototype.catch()

This method is used to add a reject handler to a contract, which accepts only one parameter: onReject. Call 'promise.prototype. Then (null, onRejected)'Copy the code

In return for new contract instances, promise.prototype.catch () is the same as onRejected for promise.prototype.then ()

let p1 = new Promise(() = > {});
let p2 = p1.catch();
setTimeout(() = > {
    console.log(p1) // Promise {<pending>}
}, 0);
setTimeout(() = > {
    console.log(p2) // Promise {<pending>}
}, 0);
setTimeout(() = > {
    console.log(p1 === p2) // false
}, 0);
Copy the code

Promise.prototype.finally()

This method will execute regardless of the date transition to resolve or reject. This avoids redundant code in the onResolved and onRejected handlers, but it has no way of knowing whether the date status will be resolved or rejectedCopy the code

Promise. Prototype. Finally () returns the new period of instance is different from then () or catch () method returns the instance. Because onFinally is designed to be a state-independent method, it behaves as a parent contract delivery in most cases, both for resolved and rejected states

let p1 = Promise.resolve('foo');

let p2 = p1.finally();
let p3 = p1.finally(() = > undefined)
let p4 = p1.finally(() = > {})
let p5 = p1.finally(() = > Promise.resolve())
let p6 = p1.finally(() = > 'bar')
let p7 = p1.finally(() = > Promise.resolve('bar'))
let p8 = p1.finally(() = > Error('qux'))

setTimeout(() = > {
    console.log(p1) //Promise {<fulfilled>: "foo"}
    console.log(p2) //Promise {<fulfilled>: "foo"}
    console.log(p3) //Promise {<fulfilled>: "foo"}
    console.log(p4) //Promise {<fulfilled>: "foo"}
    console.log(p5) //Promise {<fulfilled>: "foo"}
    console.log(p6) //Promise {<fulfilled>: "foo"}
    console.log(p7) //Promise {<fulfilled>: "foo"}
    console.log(p8) //Promise {<fulfilled>: "foo"}
}, 0);
Copy the code

If it returns a pending date, or if onFinally throws an error (showing that it threw an error or returning a reject date), the corresponding expiration (pending or rejected) will be returned.

let p9 = p1.finally(() = > new Promise(() = > {}))
let p10 = p1.finally(() = > Promise.reject())

setTimeout(() = > {
    console.log(p9) //Promise {<pending>}
    console.log(p10) //Promise {<rejected>: undefined}
}, 0);

let p11 = p1.finally(() = > {throw 'baz'});
setTimeout(() = > {
    console.log(p11) //Promise {<rejected>: "baz"}
}, 0);
Copy the code

It is unusual to return a pending contract, because once the contract is resolved, the new contract will still be passed to the original one as is

let p1 = Promise.resolve('foo');

let p2 = p1.finally(() = > {
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('bar')},100); })})setTimeout(() = > {
    console.log(p2) //Promise {<pending>}
}, 0);
setTimeout(() = > {
    console.log(p2) //Promise {<fulfilled>: "foo"}
}, 200);
Copy the code

Non-reentrant date reduction methods

Js elevation does not speak human language, simply put, then method is asynchronous

let syncResolve;

let p = new Promise((resolve) = > {
    syncResolve = function () {
        console.log(1)
        resolve()
        console.log(2)
    }
})
p.then(() = > {
    console.log(4)
})
syncResolve()
console.log(3)
Copy the code

Similarly, onResolved onRejected, catch, finally has not reentrant

Proximity handler

That is, when multiple handlers are added to a contract, when the contract state changes, the related handlers are executed in the order in which they were added

Pass resolution values and rejection reasons

This means that resolve or reject can be passed

Reject the date and reject error handling

An error thrown in the execution function or handler of the contract causes the rejection and the corresponding error object becomes the reason for the rejectionCopy the code
let p1 = new Promise((resolve, reject) = > {
    reject(Error('foo'))})let p2 = new Promise((resolve, reject) = > {
    throw Error('foo')})let p3 = Promise.resolve().then(() = > {
    throw Error('foo')})let p4 = Promise.reject(Error('foo'))
setTimeout(() = > {
    console.log(p1)
}, 0);
setTimeout(() = > {
    console.log(p2)
}, 0);
setTimeout(() = > {
    console.log(p3)
}, 0);
setTimeout(() = > {
    console.log(p4)
}, 0);
Copy the code

Promise.resolve().then()The error occurs last because it requires adding handlers to the runtime message queue, i.ePromise.resolve()A new term contract is then created and executedthenMethod, which is then added to the queue

Normally, when an error is thrown with the throw() keyword, subsequent code is not executed, but an error thrown in a contract is thrown asynchronously, so it does not prevent synchronous code execution

Promise.reject(Error('foo'))
console.log('bar')/ /
Copy the code

Asynchronous errors can only be caught through the asynchronous onRejected handler

Promise.reject(Error('foo')).catch((e) = > {
    console.log(e)
})
Copy the code

While a contract is in a pending state, it can still be caught using try/catch

let p = new Promise((resolve, reject) = > {
    try {
        throw Error('foo')}catch(e) {
        console.log(e)
    }
    resolve('bar')})setTimeout(() = > {
    console.log(p)
}, 0);
Copy the code

The onRejected handlers for then() and catch() are semantically equivalent to try/catch. The point of both is to catch an error and isolate it without affecting normal logic. So the onRejected handler returns a resolution date after catching an asynchronous error

try/catch

console.log(1)
try{
    throw Error('foo')}catch(e) {
    console.log(e)
}
console.log(2)
Copy the code

onRejected

new Promise((resolve, reject) = > {
    console.log(1)
    reject(Error('bar'))
}).catch((e) = > {
    console.log('catch', e)
}).then(() = > {
    console.log('then')})Copy the code

Term linkage and term synthesis

  • Term linkage: the joining of one term after another
  • Term composition: To combine multiple terms into one term
function delayedResolve(str) {
    return new Promise((resolve, reject) = > {
        console.log(str)
        setTimeout(() = > {
            resolve()
        }, 1000);
    })
}
delayedResolve('p1').then(() = > {
    return delayedResolve('p2')
}).then(() = > {
    return delayedResolve('p3')
}).then(() = > {
    return delayedResolve('p4')})/ / p1 after 1 s
/ / p2 after 2 s
/ / p3 after 3 s
/ / p4 after 4 s
Copy the code

This takes care of callback hell

Promise.all() and promise.race () are static methods of combining multiple term instances into a single term, and the rows of the combined term depend on the behavior of the internal term

Promise.all()

  • This method takes an iterable and returns a new date
// Return a new contract
let p1 = Promise.all([
    Promise.resolve(),
    Promise.resolve()
])
// Elements in an iterable are converted to a contract by promise.resolve ()
let p2 = Promise.all([1.2])
// An empty iterable is equivalent to promise.resolve ()
let p3 = Promise.all([])
setTimeout(() = > {
    console.log(p1)
    console.log(p2)
    console.log(p3)
}, 0);
Copy the code

Nothing is invalid

  • Composite appointments are not resolved until all appointments have been resolved
let p = Promise.all([
    Promise.resolve(),
    new Promise((resolve, reject) = > {
        setTimeout(() = > {
            resolve()
        }, 1000); })])setTimeout(() = > {
    console.log(p) //Promise {<pending>}
}, 0);

p.then(() = > {
    console.log("all resolved") / / 1 s after execution
})
Copy the code
  • If there is a term to be determined, then the composite term is also determined
// Used for pending state
let p1 = Promise.all([new Promise(() = > {}), Promise.resolve()])
setTimeout(() = > {
    console.log(p1) // Promise {<pending>}
}, 0);
Copy the code
  • If there is a term that is rejected, then the composite term is also rejected
// Result in rejection
let p2 = Promise.all([
    Promise.resolve(),
    Promise.reject(),
    Promise.resolve()
])
setTimeout(() = > {
    console.log(p2)
}, 0);
Copy the code

  • If all terms are resolved, the solution of the synthesized term is an array containing the solution values of the term, in iterator order
let p1 = Promise.all([
    Promise.resolve(3),
    Promise.resolve(),
    Promise.resolve(4)
])
p1.then((val) = > {
    console.log(val) //[3, undefined, 4]
})
Copy the code
  • If a fixed-term contract is rejected, the first term will use its own reasons as the reason for the refusal of the composite term, and the subsequent terms will not affect the reason for the refusal of the final term. However, this does not affect the normal rejection of all inclusive contracts
let p1 = Promise.all([
    Promise.reject(3),
    new Promise((resolve, reject) = > {
        setTimeout(() = > {
            reject()
        }, 1000);
    })
])
p1.catch((reason) = > {
    console.log(reason) / / 3
})
Copy the code

Promise.race()

  • Parameter takes an iterable and returns a new date
  • The term mirror of a set of sets is returned first whose term mirror results first
let p1 = Promise.race([
    Promise.resolve(),
    Promise.resolve()
])
// The iterable will be converted by promise.resolve ()
let p2 = Promise.race([3.4])
// Empty iterables are equivalent to new Promise(() => {})
let p3 = Promise.race([])
setTimeout(() = > {
    console.log(p1) // Promise {<fulfilled>: undefined}
    console.log(p2) // Promise {<fulfilled>: 3}
    console.log(p3) // Promise {<pending>}
}, 0);
Copy the code

Nothing is invalid

  • The term mirror of a set of sets is returned first whose term mirror results first
// Resolve occurs first, rejection after timeout is ignored
let p1 = Promise.race([
    Promise.resolve(3),
    new Promise((resolve, reject) = > {
        setTimeout(() = > {
            reject()
        }, 1000); })])// Reject occurs first, and resolution after timeout is ignored
let p2 = Promise.race([
    Promise.reject(3),
    new Promise((resolve, reject) = > {
        setTimeout(() = > {
            resolve()
        }, 1000); })])// Who decides whose mirror is returned first
let p3 = Promise.race([
    Promise.resolve(5),
    Promise.resolve(6),
    Promise.resolve(7)])setTimeout(() = > {
    console.log(p1) // Promise {<fulfilled>: 3}
    console.log(p2) // Promise {<rejected>: 3}
    console.log(p3) // Promise {<fulfilled>: 5}
}, 0);
Copy the code
  • If the first fixed term is rejected, it will be the reason for rejecting the composite term, and the subsequent rejected terms will not affect the reason for rejecting the final term. However, it does not affect the normal rejection operations of all inclusive terms. Similar to promise.all (), the synthesized date silently processes all inclusive term rejection operations
let p = Promise.race([
    Promise.reject(3),
    new Promise((resolve, reject) = > {
        reject() // This rejection is processed silently
        
    }, 1000)
])
p.catch((reason) = > {
    console.log(reason)/ / 3
})
Copy the code

Futures extended

There are two main extensions

  • Futures to cancel
  • Progress of the track

Futures to cancel

  • Third party library: Bluebird
  • Self-implementation: Cancel token

Start by implementing a basic instance of the CancelToken class

class CancelToken{
    constructor(cancelFn) {
        this.promise = new Promise((resolve, reject) = > {
            cancelFn(resolve)
        })
    }
}
Copy the code

This class of wrapper exposes the solution to the cancelFn parameter for a period of time. External code can pass a function to the constructor to control when the date can be cancelled.

<button id="start">Start</button>
<button id="cancel">Cancel</button>
<script>
class CancelToken{
    constructor(cancelFn) {
        this.promise = new Promise((resolve, reject) = > {
            cancelFn(() = > {
                console.log("delay cancelled")
                resolve()
            })
        })
    }
}

const startBtn = document.querySelector("#start")
const cancelBtn = document.querySelector("#cancel")

function cancellableDelayResolve(delay) {
    console.log("set delay")

    return new Promise((resolve, reject) = > {
        const id = setTimeout(() = > {
            console.log("delayed resolve")
            resolve()
        }, delay)

        const cancelToken = new CancelToken((cancelCallback) = > {
            cancelBtn.addEventListener("click", cancelCallback)
        })
        cancelToken.promise.then(() = > {
            clearTimeout(id)
        })
    })
}

startBtn.addEventListener("click".() = > {
    cancellableDelayResolve(3000).then(() = > {
        console.log("I did it.")})})</script>
Copy the code

Notice of progress of the contract

Contracts in execution may have a number of discrete “phases” that must be passed before final resolution. There are situations where it is useful to monitor the progress of the contract execution, but ES6 does not support progress tracking and we can extend the implementation ourselves

Extend the Promise class by adding a notify() method to it

class TrackablePromise extends Promise {
    constructor(executor) {
        const notifyHandlers = [];
        super((resolve, reject) = > {
            return executor(resolve, rejecty, (status) = > {
                notifyHandlers.map((handler) = > {
                    return handler(status)
                })
            })
        })

        this.notifyHandlers = notifyHandlers;
    }

    notify(notifyHandler) {
        this.notifyHandles.push(notifyHandler)
        return this}}Copy the code

I don’t get it. I’ll study it later