Focus onThe front small OuRead more original technical articles
futures
- A contract is a stand-in for a result that does not yet exist, a mechanism for asynchronous program execution
Related codes →
Promises/A + specification
- ES6 added
Promise
Type, which became the dominant asynchronous programming mechanism, and all modern browsers support scheduling
Futures basis
Promise
Type bynew
Operator to instantiateExecutor functionsAs a parameter
// let p = new Promise() // TypeError: Promise resolver undefined is not a function
let p = new Promise(() = > {})
setTimeout(console.log, 0, p) // Promise { <pending> }
Copy the code
Schedule state machine
- A contract is a stateful object:
- To be determined
pending
saidNot commenced or being executed. The initial state can be either the cash or the reject state,It’s not reversible - cash
fullfilled
(or solveresolved
) indicates successful completion - Refused to
rejected
Indicates that the completion was not successful
- To be determined
- The state of the contract is private, encapsulating asynchronous behavior to isolate external code and cannot be read or modified by external JS code
Resolve values, rejection reasons, and contract use cases
- A scheduled state machine can provide useful information if it sends an HTTP request to the server:
- Returns a status code in the range 200-299 to change the contract status to “cashed”, dueinternalreceivedprivatethe
JSON
The default value is undefined - Return a status code that is not in the range 200-299, and the contract status will be changed to “Rejected”internalreceivedprivatethe
Error
Object (containing error messages) with the default value undefined
- Returns a status code in the range 200-299 to change the contract status to “cashed”, dueinternalreceivedprivatethe
Control contract state by performing functions
- The state of the contract is private and can only be performed internally in the executor function
- The executor function is responsibleThe initialization period is about asynchronous behaviorandControl state transition:
- through
resolve()
andreject()
Two function parameters control state transitions resolve()
Will change the state toexchange.reject()
Will change the state toRefused toandThrow an error
- through
let p1 = new Promise((resolve, reject) = > resolve())
setTimeout(console.log, 0, p1) // Promise {<fulfilled>: undefined}
let p2 = new Promise((resolve, reject) = > reject())
setTimeout(console.log, 0, p2) // Promise {<rejected>: undefined}
// Uncaught (in promise)
Copy the code
- An executor function is a contract initializer that executes synchronously
new Promise(() = > setTimeout(console.log, 0.'executor'))
setTimeout(console.log, 0.'promise initialized')
/* 'executor', print 'Promise initialized', print */
Copy the code
- You can add
setTimeout
Postponing the switch state of the actuator function
let p3 = new Promise((resolve, reject) = > {
setTimeout(resolve, 1000)})setTimeout(console.log, 0, p3) // Promise {
}, the internal callback will not be executed when instance P3 is printed
Copy the code
resolve()
andreject()
Whichever is called, the state transition isAn irrevocable, continue to modify the statusSilent failure
let p4 = new Promise((resolve, reject) = > {
resolve()
reject() // Silence failed
})
setTimeout(console.log, 0, p4) // Promise {<fulfilled>: undefined}
Copy the code
- To avoid getting stuck in the pending state, you can add a timed exit function that will reject the callback after a certain number of hours
let p5 = new Promise((resolve, reject) = > {
setTimeout(reject, 10000) Call reject() after 10 seconds
})
setTimeout(console.log, 0, p5) // Promise {
}, resolve() will not be called for 10 seconds
setTimeout(console.log, 11000, p5) // Promise {
: undefined}, reject()
// Uncaught (in promise)
Copy the code
Promise.resolve()
- call
Promise.resolve()
Method, caninstantiationaTo solve thefutures
let p6 = new Promise((resolve, reject) = > {
resolve()
})
console.log(p6) // Promise {<fulfilled>: undefined}
let p7 = Promise.resolve()
console.log(p7) // Promise {<fulfilled>: undefined}
Copy the code
- To pass to
Promise.resolve()
theThe first parameterIs the value of the settled term
setTimeout(console.log, 0.Promise.resolve()) // Promise {<fulfilled>: undefined}
setTimeout(console.log, 0.Promise.resolve(3)) // Promise {<fulfilled>: 3}
setTimeout(console.log, 0.Promise.resolve(4.5.6)) // Promise {< depressing >: 4}, only take the first parameter
Copy the code
Promise.resolve()
Is aPower etc.Method, if passed inThe parameter is a term contractAnd its behavior resembles that of aEmpty package
let p8 = Promise.resolve(7)
setTimeout(console.log, 0.Promise.resolve(p8)) // Promise { 7 }
setTimeout(console.log, 0, p8 === Promise.resolve(p8)) // true
setTimeout(console.log, 0, p8 === Promise.resolve(Promise.resolve(p8))) // true
Copy the code
- This idempotency preserves the state of the input contract
let p9 = new Promise(() = > {}) // To be determined
setTimeout(console.log, 0, p9) // Promise { <pending> }
setTimeout(console.log, 0.Promise.resolve(p9)) // Promise { <pending> }
setTimeout(console.log, 0.Promise.resolve(Promise.resolve(p9))) // Promise { <pending> }
Copy the code
- This method is able to wrap any out-of-date value (including error objects) and convert it into a resolved term, which may result in unexpected behavior
let p10 = Promise.resolve(new Error('foo'))
setTimeout(console.log, 0, p10) // Promise {<fulfilled>: Error: foo
Copy the code
Promise.reject()
- with
Promise.resolve()
Similar,Promise.reject()
caninstantiationarefuseFutures andThrows an asynchronous error- The errorCan’tthrough
try/catch
Catch, can only be caught by a rejection handler
- The errorCan’tthrough
let p11 = new Promise((resolve, reject) = > {
reject()
})
console.log(p11) // Promise {<rejected>: undefined}
// Uncaught (in promise)
let p12 = Promise.reject()
console.log(p12) // Promise {<rejected>: undefined}
// Uncaught (in promise)
Copy the code
- To pass to
Promise.resolve()
theThe first parameterThis parameter will also be used as a reason for rejecting the termPass to a subsequent rejection handler
let p13 = Promise.reject(3)
setTimeout(console.log, 0, p13) // Promise { <rejected> 3 }
p13.then(null.(err) = > setTimeout(console.log, 0, err)) // 3, the argument is passed to the subsequent rejection handler
Copy the code
Promise.reject()
Not idempotent (andPromise.resolve()
Different), if the parameter is a date, the date becomes the rejection reason for the return
setTimeout(console.log, 0.Promise.reject(Promise.resolve())) // Promise {<rejected>: Promise}
Copy the code
Duality of synchronous/asynchronous execution
- Due to contractAsynchronous natureWhile it is a synchronous object (which can be used in synchronous execution mode), it is also a medium for asynchronous execution mode
- The synchronous thread’s code cannot catch rejected terms, and rejection errors are handled through the browser asynchronous message queue
- Once code starts executing in asynchronous mode, the only way to interact with it is to use an asynchronous structure, immediate
try {
throw new Error('foo') // The synchronization thread threw an error
} catch (error) {
console.log(error + '1') // Error: foo1, synchronization thread caught an Error
}
try {
Promise.reject('bar') // Synchronize thread usage duration
} catch (error) {
console.log(error + '2') // The synchronous thread could not catch the rejected date
}
// Promise {
: "bar"}, the browser asynchronous message queue caught the rejected date
// Uncaught (in promise) bar
Copy the code
Instance method of a contract
- These methods can access the data returned by asynchronous operations and process the results of both success and failure
Implement the Thenable interface
- Any object has one of the asynchronous constructs exposed by ECMAScript
then()
Method, which is considered implementedthenable
interface
class MyThenable {
// The simplest class to implement the Thenable interface
then(){}}Copy the code
Promise.prototype.then()
Promise.prototype.then()
Approx.Add handlerTo receiveTwo optional handler parametersonResolved
andonRejected
onResolved
Will enter during the contractcashState time executiononRejected
Will enter during the contractRefused toState time execution
function onResolved(id) {
setTimeout(console.log, 0, id, 'resolved')}function onRejected(id) {
setTimeout(console.log, 0, id, 'rejected')}let p14 = new Promise((resolve, reject) = > {
setTimeout(resolve, 3000)})let p15 = new Promise((resolve, reject) = > {
setTimeout(reject, 3000)
})
p14.then(
() = > {
onResolved('p14') // 'P14 resolved' (3 seconds later)
},
() = > {
onRejected('p14')
}
)
p15.then(
() = > {
onResolved('p15')},() = > {
onRejected('p15') // 'p15 Rejected '(3秒)})Copy the code
- To pass to
then()
Any arguments that are not of function type are silently ignored (not recommended) if only providedonResolved
oronRejected
, generally passed in at another parameter locationnull
orundefined
p14.then('gobbeltygook') // Arguments are not objects, silently ignored
p14.then(() = > onResolved('p14')) // 'P14 resolved' (3 seconds later), onRejected
p15.then(null.() = > onRejected('p15')) // 'p15 Rejected '(3 seconds later), onResolved
Copy the code
Promise.prototype.then()
Returns aNew contract instance, the instance is based ononResolved
Handler (Promise.resolved()
Wrap) to build the return value- If this handler is not provided, wrap the value after the settlement of the previous term (passing of the parent term)
- If a handler is provided, howeverNo return statement displayedWraps the default return value
undefined
- If a handler is provided and there is a displayed return value, wrap the value
- If a handler is provided and a return date is returned, wrap the returned date
- If a handler is provided and an exception is thrown, the rejected contract is wrapped
- If a handler is provided and an error value is returned, wrap the error object in a resolved term (not a rejected term)
let p16 = Promise.resolve('foo')
let result1 = p16.then() // No handler is provided
setTimeout(console.log, 0, result1) // Promise {< depressing >: 'foo'}, wrap the value after the last term is fulfilled
let result2 = p16.then(() = > undefined) // The handler has no return statement to display
let result3 = p16.then(() = > {}) // The handler has no return statement to display
let result4 = p16.then(() = > Promise.resolve()) // The handler has no return statement to display
setTimeout(console.log, 0, result2) // Promise {< depressing >: undefined}, packaging default return value undefined
setTimeout(console.log, 0, result3) // Promise {< depressing >: undefined}, packaging default return value undefined
setTimeout(console.log, 0, result4) // Promise {< depressing >: undefined}, packaging default return value undefined
let result5 = p16.then(() = > 'bar') // The handler has a displayed return value
let result6 = p16.then(() = > Promise.resolve('bar')) // The handler has a displayed return value
setTimeout(console.log, 0, result5) // Promise {< depressing >: 'bar'}, wrap this value
setTimeout(console.log, 0, result6) // Promise {< depressing >: 'bar'}, wrap this value
let result7 = p16.then(() = > new Promise(() = > {})) // The handler returns a pending date
let result8 = p16.then(() = > Promise.reject('bar')) // The handler returns a rejected date
// Uncaught (in promise) bar
setTimeout(console.log, 0, result7) // Promise {
}, the package returns the date
setTimeout(console.log, 0, result8) // Promise {
: 'bar'}
let result9 = p16.then(() = > {
throw 'baz' The handler throws an exception
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result9) // Promise {
: 'baz'}
let result10 = p16.then(() = > Error('qux')) // The handler returns an error
setTimeout(console.log, 0, result10) This is a big pity. // Promise {< depressing >: Error: qux}, wrap the Error object in a solved term
Copy the code
- with
onResolved
The same,onRejected
When a handler is used as a parameter, its return value is alsoPromise.resolve()
Wrap, returnNew contract instance
let p17 = Promise.reject('foo')
let result11 = p17.then() // No handler is provided
// Uncaught (in promise) foo
setTimeout(console.log, 0, result11) // Promise {
: 'foo'}, package the value after the last contract is resolved
let result12 = p17.then(null.() = > undefined) // The handler has no return statement to display
let result13 = p17.then(null.() = > {}) // The handler has no return statement to display
let result14 = p17.then(null.() = > Promise.resolve()) // The handler has no return statement to display
setTimeout(console.log, 0, result12) // Promise {< depressing >: undefined}, packaging default return value undefined
setTimeout(console.log, 0, result13) // Promise {< depressing >: undefined}, packaging default return value undefined
setTimeout(console.log, 0, result14) // Promise {< depressing >: undefined}, packaging default return value undefined
let result15 = p17.then(null.() = > 'bar') // The handler has a displayed return value
let result16 = p17.then(null.() = > Promise.resolve('bar')) // The handler has a displayed return value
setTimeout(console.log, 0, result15) // Promise {< depressing >: 'bar'}, wrap this value
setTimeout(console.log, 0, result16) // Promise {< depressing >: 'bar'}, wrap this value
let result17 = p17.then(null.() = > new Promise(() = > {})) // The handler returns a pending date
let result18 = p17.then(null.() = > Promise.reject('bar')) // The handler returns a rejected date
// Uncaught (in promise) bar
setTimeout(console.log, 0, result17) // Promise {
}, the package returns the date
setTimeout(console.log, 0, result18) // Promise {
: 'bar'}
let result19 = p17.then(null.() = > {
throw 'baz' The handler throws an exception
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result19) // Promise {
: 'baz'}
let result20 = p17.then(null.() = > Error('qux')) // The handler returns an error
setTimeout(console.log, 0, result20) This is a big pity. // Promise {< depressing >: Error: qux}, wrap the Error object in a solved term
Copy the code
Promise.prototype.catch()
Promise.prototype.catch()
Approx.Add a rejection handlerTo receive1 optional handler parameteronRejected
- This method is equivalent to calling
Promise.prototypr.then(null, onRejected)
- The method method also returnsNew contract instance, its behavior and
Promise.prototype.then()
theonRejeted
Same handler
- This method is equivalent to calling
let p18 = Promise.reject()
let onRejected2 = function () {
setTimeout(console.log, 0.'reject')
}
p18.then(null, onRejected2) // 'reject'
p18.catch(onRejected2) // 'reject', both methods of adding a rejection handler are the same
Copy the code
Promise.prototype.finally()
Promise.prototype.finally()
Approx.Add an onFinally handlerTo receive1 optional handler parameteronFinally
- Regardless of the period about convert toTo solveorRefused toState,
onFinally
The handlerWill perform, but it cannot know the state of the contract - This method is primarily used to add cleanup code
- Regardless of the period about convert toTo solveorRefused toState,
let p19 = Promise.resolve()
let p20 = Promise.reject()
let onFinally = function () {
setTimeout(console.log, 0.'Finally')
}
p19.finally(onFinally) // 'Finally'
p20.finally(onFinally) // 'Finally'
Copy the code
Promise.prototype.finally()
Returns aNew contract instance, the following cases are packagedThe passing of paternal contracts- No handler is provided
- A handler is provided, but no return statement is displayed
- A handler is provided with a displayed return value
- The handler returns a resolution date
- The handler returned an error value
let p21 = Promise.resolve('foo')
let result23 = p21.finally() // No handler is provided
let result24 = p21.finally(() = > undefined) // A handler is provided, but no return statement is displayed
let result25 = p21.finally(() = > {}) // A handler is provided, but no return statement is displayed
let result26 = p21.finally(() = > Promise.resolve()) // A handler is provided, but no return statement is displayed
let result27 = p21.finally(() = > 'bar') // A handler is provided with a displayed return value
let result28 = p21.finally(() = > Promise.resolve('bar')) // The handler returns a resolution date
let result29 = p21.finally(() = > Error('qux')) // The handler returns an error
setTimeout(console.log, 0, result23) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result24) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result25) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result26) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result27) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result28) // Promise {< depressing >: 'foo'}, this is a big pity
setTimeout(console.log, 0, result29) // Promise {< depressing >: 'foo'}, this is a big pity
Copy the code
- if
onFinally
Handler returnPending or rejected termsorThrow an error, returns the value wrapperThe corresponding term(Throws the wrong wrapper rejected terms)
let result30 = p21.finally(() = > new Promise(() = > {})) // The handler returns a pending date
let result31 = p21.finally(() = > Promise.reject()) // The handler returns a rejected date
// Uncaught (in promise) undefined
let result32 = p21.finally(() = > {
throw 'baz' // The handler threw an error
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result30) // Promise {
} returns the corresponding date
setTimeout(console.log, 0, result31) // Promise {
: undefined
setTimeout(console.log, 0, result32) // Promise {
: 'baz'
Copy the code
onFinally
Handler returnWhen the pending contract is settled, the new contract instance is still passed back to the original contract
let p22 = Promise.resolve('foo')
let p23 = p22.finally(
() = > new Promise((resolve, reject) = > setTimeout(() = > resolve('bar'), 100)) // The handler returns a pending period (resolved after 100 milliseconds)
)
setTimeout(console.log, 0, p23) // Promise {
} returns the corresponding date
setTimeout(() = > setTimeout(console.log, 0, p23), 200) // Promise {< big song >: "foo"}, (200 ms later) The Promise is fulfilled
Copy the code
Non-reentrant date reduction methods
- When a contract enters a settled (resolve/reject) state, the handler associated with that state is not executed immediately, and the synchronization code after the handler is executed before it, a feature known as non-reentrant
let p24 = Promise.resolve() // The date of settlement has been set
p24.then(() = > console.log('onResolved handler')) // The onResolved handler associated with the date status
console.log('then() returns') // The synchronization code after the handler
/* 'onResolved handler' */
Copy the code
- Even if the contract changes state (resolved/rejected) after the handler, the handler still behaves non-reentrant
let synchronousResolve // Global method: schedule state state
let p25 = new Promise((resolve) = > {
synchronousResolve = function () {
console.log('1: invoking resolve()')
resolve() // Schedule status changes
console.log('2: resolve() returns')
}
})
p25.then(() = > console.log('4: then() handler executes')) // The onResolved handler associated with the date status
synchronousResolve() // Synchronization code after handler: contract state changes
console.log('3: synchronousResolve() returns') // The synchronization code after the handler
/* '1: invoking resolve()' '2: resolve() returns' '3: synchronousResolve() returns' '4: then() handler executes' */
Copy the code
- The non-reentrant feature applies
onResolved
,onRejected
,catch()
,finally()
The handler
let p26 = Promise.resolve()
p26.then(() = > console.log('p26.then() onResolved'))
console.log('p26.then() returns')
let p27 = Promise.reject()
p27.then(null.() = > console.log('p27.then() onRejected'))
console.log('p27.then() returns')
let p28 = Promise.reject()
p28.catch(() = > console.log('p28.catch() onRejected'))
console.log('p28.catch() returns')
let p29 = Promise.resolve()
p26.finally(() = > console.log('p29.finally() onFinally'))
console.log('p29.finally() returns')
/* 'p26.then() returns' 'p27.then() returns' 'p28.catch() returns' 'p29.finally() returns' 'p26.then() onResolved' 'p27.then() onRejected' 'p28.catch() onRejected' 'p29.finally() onFinally' */
Copy the code
The execution order of adjacent handlers
- If the term contract is addedMultiple handlersWhen the contract status changes, the processing program pressesAdd the orderExecuted in sequence
then()
,catch()
,finally()
This is true for all added handlers
let p30 = Promise.resolve()
let p31 = Promise.reject()
p30.then(() = > setTimeout(console.log, 0.1))
p30.then(() = > setTimeout(console.log, 0.2))
p31.then(null.() = > setTimeout(console.log, 0.3))
p31.then(null.() = > setTimeout(console.log, 0.4))
p31.catch(() = > setTimeout(console.log, 0.5))
p31.catch(() = > setTimeout(console.log, 0.6))
p30.finally(() = > setTimeout(console.log, 0.7))
p30.finally(() = > setTimeout(console.log, 0.8))
/* 1 2 3 4 5 6 7 8 */
Copy the code
Pass resolution values and rejection reasons
- Futures intoSettle (settle/reject)State is provided to the handlerSettlement value (cashed)orReason for refusal (Refusal)
- inExecutive function, the solution value and the rejection reason are respectively
resolve()
andreject()
theThe first parameterAnd send it toonResolved
andonRejected
Handler (as itsThe only parameter) - in
Promise.resolve()
andPromise.reject()
When called, the resolution value received and the rejection reason are also passed back to the handler (as itsThe only parameter)
- inExecutive function, the solution value and the rejection reason are respectively
let p32 = new Promise((resolve, reject) = > resolve('foo')) // Execute the function
p32.then((value) = > console.log(value)) // 'foo'
let p33 = new Promise((resolve, reject) = > reject('bar')) // Execute the function
p33.catch((reason) = > console.log(reason)) // 'bar'
let p34 = Promise.resolve('foo') / / Promise. Resolve ()
p34.then((value) = > console.log(value)) // 'foo'
let p35 = Promise.reject('bar') / / Promise. Reject ()
p35.catch((reason) = > console.log(reason)) // 'bar'
Copy the code
Reject terms and reject error handling
- Throwing an error in an execution function or handler of a contract results in rejection, and the error object becomes the reason for rejection
let p36 = new Promise((resolve, reject) = > reject(Error('foo'))) // Throw an error in the execution function
let p37 = new Promise((resolve, reject) = > {
throw Error('foo') // Throw an error in the execution function
})
let p38 = Promise.resolve().then(() = > {
throw Error('foo') // Throw an error in the handler
})
let p39 = Promise.reject(Error('foo')) // Throw an error in rejected contracts
setTimeout(console.log, 0, p36) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p37) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p38) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p39) // Promise {<rejected>: Error: foo
Copy the code
- Be able toFor any reasonRejection, including
undefined
, but the bestUse error objects uniformlyThe error object can be caught by the browserStack trace information - Four uncaught errors will be thrown in the browser if the rejection period is described above:
Promise.resolve().then()
The error appears last because it is needed at run timeAdd handler(that is, create another new term before capturing)
/* Uncaught (in promise) Error: foo at
:1:51 at new Promise (
) at
:1:11 (anonymous) @ VM1402:1 (anonymous) @ VM1402:1 Uncaught (in promise) Error: foo at
:3:9 at new Promise (
) at
:2:11 (anonymous) @ VM1402:3 (anonymous) @ VM1402:2 Uncaught (in promise) Error: foo at
:8:26 (anonymous) @ VM1402:8 Uncaught (in promise) Error: foo at
:6:9 (anonymous) @ VM1402:6 Promise.then (async) (anonymous) @ VM1402:5 */
Copy the code
- Asynchronous errorThe mechanism is different from synchronization:
- Sync code pass
throw()
If the keyword throws an error, the system stops executing any subsequent commands - The synchronization instruction is not blocked when an error is thrown in a contracterrorIt can only be done asynchronously
onRejected
The handlercapture
- Sync code pass
throw Error('foo') // Synchronization code throws an error (try/catch can catch)
console.log('bar') // No further instructions will be executed
// Uncaught Error: foo, browser message queue
Promise.reject(Error('foo')) // An error is thrown in a contract (not caught in a try/catch)
console.log('bar') // 'bar', the synchronization command continues
// Uncaught (in promise) Error: foo, browser message queue
Promise.reject(Error('foo')).catch((e) = > {
console.log(e) // 'Error: foo', captured in the contract
})
Copy the code
- Executive functionThe error inBefore settling or rejecting the contract, it can still be used
try/catch
capture
let p40 = new Promise((resolve, reject) = > {
try {
throw Error('foo')}catch (error) {}
resolve('bar')})setTimeout(console.log, 0, p40) // Promise {<fulfilled>: 'bar'}
Copy the code
then()
andcatch()
theonRejected
The handler is semantically related totry/catch
Same (errors are isolated after they are caught without affecting normal logic), soonReject
After the handler catches an asynchronous errorReturn a settled term
console.log('begin synchronous execution')
try {
throw Error('foo') // Throws a synchronization error
} catch (error) {
console.log('caught error', error) // Catch synchronization errors
}
console.log('continue synchronous execution')
/* 'begin synchronous execution' 'caught error Error: foo' 'continue synchronous execution' */
new Promise((resolve, reject) = > {
console.log('begin synchronous execution')
reject(Error('bar')) // Throw an asynchronous error
})
.catch((e) = > {
console.log('caught error', e) // Catch asynchronous errors
})
.then(() = > {
console.log('continue synchronous execution')})/ *Copy the code
Term linkage and term synthesis
- Multiple terms together can form powerful code logic: term linkage (splicing) and term composition (composition)
Futures chain
- Instance method for each term (
then()
,catch()
,finally()
) all return new instances of terms, and multiple terms can be formed by concatenated callsFutures chain
let p41 = new Promise((resolve, reject) = > {
console.log('first')
resolve()
})
p41
.then(() = > console.log('second'))
.then(() = > console.log('third'))
.then(() = > console.log('fourth'))
/* 'first' 'second' 'third' 'fourth' */
Copy the code
- If you want to serialize an asynchronous task, you need to have each executor return a periodic instance,
let p42 = new Promise((resolve, reject) = > {
console.log('p42 first')
setTimeout(resolve, 1000)
})
p42
.then(
() = >
// The executor returns a scheduled instance
new Promise((resolve, reject) = > {
console.log('p42 second')
setTimeout(resolve, 1000)
})
)
.then(
() = >
// The executor returns a scheduled instance
new Promise((resolve, reject) = > {
console.log('p42 third')
setTimeout(resolve, 1000)
})
)
.then(
() = >
// The executor returns a scheduled instance
new Promise((resolve, reject) = > {
console.log('p42 fourth')
setTimeout(resolve, 1000)}))/*
'p42 first'(1秒后)
'p42 second'(2秒后)
'p42 third'(3秒后)
'p42 fourth'(4秒后)
*/
Copy the code
- You can wrap the same code that generates the contract into a factory function
function delayedResolve(str) {
return new Promise((resolve, reject) = > {
console.log(str)
setTimeout(resolve, 1000)
})
}
delayedResolve('p42 first')
.then(() = > delayedResolve('p42 second'))
.then(() = > delayedResolve('p42 third'))
.then(() = > delayedResolve('p42 fourth'))
/*
'p42 first'(1秒后)
'p42 second'(2秒后)
'p42 third'(3秒后)
'p42 fourth'(4秒后)
*/
Copy the code
- Term chain can effectively solve the callback hell problem, if the above code does not have term as follows
function delayedNotPromise(str, callback = null) {
setTimeout(() = > {
console.log(str)
callback && callback()
}, 1000)
}
delayedNotPromise('p42 first'.() = > {
delayedNotPromise('p42 second'.() = > {
delayedNotPromise('p42 third'.() = > {
delayedNotPromise('p42 fourth'.() = >{})})})})/*
'p42 first'(1秒后)
'p42 second'(2秒后)
'p42 third'(3秒后)
'p42 fourth'(4秒后)
*/
Copy the code
then()
,catch()
,finally()
All return new contract instances, which can be chained arbitrarily
let p43 = new Promise((resolve, reject) = > {
console.log('p43')
reject()
})
p43
.catch(() = > console.log('p43 catch'))
.then(() = > console.log('p43 then'))
.finally(() = > console.log('p43 finally'))
/* 'p43' 'p43 catch' 'p43 then' 'p43 finally' */
Copy the code
Futures figure
- A term can have any number of handlers, and term chaining can build directed acyclic graphs
- A
- B
- D
- E
- C
- F
- G
Copy the code
let A = new Promise((resolve, reject) = > {
console.log('A')
resolve()
})
let B = A.then(() = > console.log('B'))
let C = A.then(() = > console.log('C'))
B.then(() = > console.log('D'))
B.then(() = > console.log('E'))
C.then(() = > console.log('F'))
C.then(() = > console.log('F'))
/* 'A' 'B' 'C' 'D' 'E' 'F' */
Copy the code
Promise. All () and Promise. Race ()
Promise.all()
To receive aiterable(must pass), return oneNew futures, its creation date is inAfter a set of contracts are all settledTo solve the- An element in an iterable passes
Promise.resolve()
Conversion duration approx. - An empty iterator is equivalent to
Promise.resolve()
- An element in an iterable passes
Promise.all([Promise.resolve(), Promise.resolve()]) // Receive a set of iterables
Promise.all([3.4]) // The elements in the iterable are transformed by promise.resolve ()
Promise.all([]) // An empty iterator is equivalent to promise.resolve ()
Promise.all() // TypeError: undefined is not iterable (cannot read property Symbol(symbol.iterator)), parameter mandatory
Copy the code
Promise.all()
The date of composition will only be inEach inclusion period is resolved afterTo solve the
let p44 = Promise.all([
Promise.resolve(),
new Promise((resolve, reject) = > setTimeout(resolve, 1000)),
])
p44.then(() = > setTimeout(console.log, 0.'all() resolved! ')) // 'all() resolved! '(1 second later, non-0 seconds, need to wait for the included period to be resolved first)
Copy the code
- If there is one contained term to be determined, the term to be determined is synthesized, and the same is true for rejection (rejection takes precedence over indeterminate)
let p45 = Promise.all([new Promise(() = > {}), Promise.resolve()]) // The included terms are to be determined
setTimeout(console.log, 0, p45) // Promise {
}, synthesize a pending date
let p46 = Promise.all([new Promise(() = > {}), Promise.reject()]) // Included terms are rejected (or pending)
// Uncaught (in promise) undefined
setTimeout(console.log, 0, p46) // Promise {
: undefined
Copy the code
- If all contained terms are successfully resolved, the resolved terms are synthesized and the resolved values are an array of all the resolved values containing the terms (in iterator order)
let p47 = Promise.all([
Promise.resolve(1),
Promise.resolve(),
Promise.resolve(3),])// All terms included are resolved
setTimeout(console.log, 0, p47) // Promise {<fulfilled>: [1, undefined, 3]}
Copy the code
- In case of rejection of a fixed-term contract, the first term appointment that rejects it will use its own reasons as the reason for rejection of the combined term contract (the subsequent reasons will not affect the reasons for rejection of the combined term contract), but will not affect the operation of rejection of the subsequent renewal contract
let p48 = Promise.all([
Promise.reject(3), // The first contract rejected for reason 3
new Promise((resolve, reject) = > setTimeout(reject, 1000.4)), // The second contract is rejected for reason 4
])
// Uncaught (in promise) 3
setTimeout(console.log, 0, p48) // Promise {
: 3
p48.catch((reason) = > setTimeout(console.log, 2000, reason)) // 3, the first rejection reason is used as the rejection reason for the composite contract, but the browser will not show the unhandled error (Uncaught (in promise) 3)
Copy the code
Promise.race()
withPromise.all()
Similarly, receive oneiterable(must pass), packaging in the collectionTo settle or reject firstThe contract resolves the value or rejection reason and returnsNew futures- An element in an iterable passes
Promise.resolve()
Conversion duration approx. - An empty iterator is equivalent to
Promise.resolve()
- The order of iteration determines the order of resolution
- The first rejected term appointment will use its own reason as the reason for rejecting the combined term appointment, but ** does not affect the rejection operation of the subsequent renewal contract
- An element in an iterable passes
Promise.race([Promise.resolve(), Promise.resolve()]) // Receive a set of iterables
Promise.race([3.4]) // The elements in the iterable are transformed by promise.resolve ()
Promise.race([]) // An empty iterator is equivalent to promise.resolve ()
// promise. all() // TypeError: undefined is not iterable (cannot read property Symbol(symbol.iterator)), parameter mandatory
let p49 = Promise.race([
Promise.resolve(3),
new Promise((resolve, reject) = > setTimeout(reject, 1000)))setTimeout(console.log, 0, p49) This is a big pity. // The Promise will happen first, and the rejection after timeout will be ignored
let p50 = Promise.race([
Promise.reject(4),
new Promise((resolve, reject) = > setTimeout(resolve, 1000)))// Uncaught (in promise) 4
setTimeout(console.log, 0, p50) // Promise {
: 4
let p51 = Promise.race([
Promise.resolve(1),
Promise.resolve(),
Promise.resolve(3),])setTimeout(console.log, 0, p51) // This is a big pity. {< depressing >: 1}, the order of iteration decides the order of decision
let p52 = Promise.race([
Promise.reject(3), // The first contract rejected for reason 3
new Promise((resolve, reject) = > setTimeout(reject, 1000.4)), // The second contract is rejected for reason 4
])
// Uncaught (in promise) 3
setTimeout(console.log, 0, p52) // Promise {
: 3
p52.catch((reason) = > setTimeout(console.log, 2000, reason)) // 3, the first rejection reason is used as the rejection reason for the composite contract, but the browser will not show the unhandled error (Uncaught (in promise) 3)
Copy the code
Serial phase contract synthesis
- Multiple functions can be composed into a single function
function addTwo(x) {
return x + 2
}
function addThree(x) {
return x + 3
}
function addFive(x) {
return x + 5
}
function addTen(x) {
return addFive(addThree(addTwo(x)))
}
console.log(addTen(7)) / / 17
Copy the code
- Similar to function synthesis, a contract can produce a value asynchronously and pass it to a handler, and a later contract can be concatenated with the return value of the previous contract
function addTen(x) {
return Promise.resolve(x).then(addTwo).then(addThree).then(addFive)
}
setTimeout(console.log, 0, addTen(8)) // Promise {<fulfilled>: 18}
addTen(8).then((result) = > console.log(result)) / / 18
Copy the code
- You can use
Array.prototype.reduce()
Shorthand for the series of these terms
function addTen(x) {
return [addTwo, addThree, addFive].reduce((pre, cur) = > {
return pre.then(cur)
}, Promise.resolve(x)) Resolve (x). The second argument is addTwo
}
setTimeout(console.log, 0, addTen(9)) // Promise {<fulfilled>: 19}
addTen(9).then((result) = > console.log(result)) / / 19
Copy the code
- This can eventually be encapsulated as a generic method
function compose(. fns) {
return (x) = >
fns.reduce((pre, cur) = > {
return pre.then(cur)
}, Promise.resolve(x))
}
addTen = compose(addTwo, addThree, addFive)
addTen(10).then((result) = > console.log(result)) / / 20
Copy the code
Futures extended
- The two features that ECMAScript does not cover are contract cancellation and progress tracking, which are implemented in many third-party contract libraries
Futures to cancel
- A temporary encapsulation can be provided to enable cancellation of the term
const startButton = document.querySelector('#start') // Start button
const cancelButton = document.querySelector('#cancel') // End button
let cancelBtnHasClickEvent = false // Whether the end button has a click event added
/* Each time the "Start" button is clicked, the CancelToken instance is re-instantiated, adding a click event to the CancelToken instance. CancelBtnHasClickEvent is added to the global variable cancelBtnHasClickEvent. Ensure that cancelToken is added only once */ when the "Start" button is clicked for the first time
The CancelToken class wraps a contract to expose the solution to the cancelFn argument
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) = > {
cancelFn(() = > {
setTimeout(console.log, 0.'delay cancelled') // Cancel the timer
resolve() // Date settled}}})})// Click the event to start the timer and instantiate a new CancelToken instance
function cancellabelDelayedResolve(delay) {
setTimeout(console.log, 0.'set delay') // Start the timer
return new Promise((resolve, reject) = > {
const id = setTimeout(() = > {
setTimeout(console.log, 0.'delay resolve') // Trigger after delay
resolve()
}, delay)
// Instantiate a new CancelToken instance
const cancelToken = new CancelToken((cancelCallback) = > {
cancelBtnHasClickEvent === false &&
cancelButton.addEventListener('click', cancelCallback) // The end button adds the click event
cancelBtnHasClickEvent = true // The end button has added a click event
})
cancelToken.promise.then(() = > clearTimeout(id)) // Trigger contract resolution in the token instance
})
}
startButton.addEventListener('click'.() = > cancellabelDelayedResolve(1000)) // Start button adds click event
Copy the code
Full file →
Notice of progress of the contract
- The ES6 does not support monitoring the execution progress of the contract. You can use an extension to monitor the progress
// Subclass TrackablePromise, inherit from the parent class Promise
class TrackablePromise extends Promise {
// Subclass constructor, takes 1 argument (executor function)
constructor(executor) {
const notifyHandlers = []
// super() calls the superclass constructor(), passing in the argument (the executor function)
super((resolve, reject) = > {
// Executes the executor() function that takes the argument passed to the TrackablePromise subclass and returns the result of the execution
return executor(resolve, reject, (status) = > {
console.log(status)
/* '80% remaining' (about 1 second) '60% remaining' (about 2 seconds) 'remaining' (about 3 seconds) 'remaining' (about 4 seconds) */
notifyHandlers.map((handler) = > {
return handler(status)
})
})
})
this.notifyHandlers = notifyHandlers
}
// Add the notify method, which takes 1 parameter (notifyHandler function)
notify(notifyHandler) {
this.notifyHandlers.push(notifyHandler)
return this}}// Create a subclass instance and pass in the argument (executor function)
let p53 = new TrackablePromise((resolve, reject, notify) = > {
function countdown(x) {
if (x > 0) {
notify(`The ${20 * x}% remaining`)
setTimeout(() = > countdown(x - 1), 1000)}else {
resolve()
}
}
countdown(5)})console.log(p53) // Promise {
, notifyHandlers: Array(0)}, TrackablePromise instance (subclass)
p53.notify((x) = > setTimeout(console.log, 0.'progress:', x)) // Call notify() of the scheduled instance, passing in the notifyHandler function.
p53.then(() = > setTimeout(console.log, 0.'completed')) // Call the then() method of the date instance, passing in the argument (onResolved handler)
/* 'progress: 80% remaining' (about 1 second later) 'progress: 60% remaining' (about 2 seconds later) 'progress: 40% remaining' (about 3 seconds later) 'progress: 80% remaining' (about 3 seconds later) 20% Remaining '(about 4 seconds later) 'completed' (about 5 seconds later) */
p53
.notify((x) = > setTimeout(console.log, 0.'a:', x))
.notify((x) = > setTimeout(console.log, 0.'b:', x)) // notice() returns the expiration date
p53.then(() = > setTimeout(console.log, 0.'completed'))
/* 'A: 80% remaining' (about 1 second later)' B: 80% remaining' (about 1 second later) 'A: 60% remaining' (about 2 seconds later) 'b: 60% remaining' (about 2 seconds later) 'a: 80% remaining' (about 2 seconds later) 40% Remaining '(about 3 seconds later)' B: 40% remaining' (about 3 seconds later) 'A: 20% remaining' (about 4 seconds later)' B: 20% remaining' (about 4 seconds later) 'completed' (about 5 seconds later) */
Copy the code
Summary & questions
- What is the Promise type? How do I create it? What are the different states?
- What are the executor functions responsible for? How can I delay its switching state? How do I avoid getting stuck in a pending state?
- How do I instantiate a resolved term? What is its value? What happens if the parameter passed is also dated?
- How do I instantiate a rejected term? What is the reason for its refusal? What happens if the parameter passed is also dated?
- Promise. Prototype. Then (), Promise. The prototype. The catch (), Promise. The prototype, the finally () the meaning of what be respectively? Which parameters are received? What are the return values of different parameters?
- How to understand the term “non-reentrant”? Which handlers does it apply to?
- If multiple handlers are added to the same term, in what order are they executed when their state changes? How do I pass the settlement value of the contract and the rejection reason to the handler?
- How to pass resolution values and rejection reasons? How do I catch an error object when an error is thrown? Why is it best to uniformly use the wrong object for rejection reasons?
- Write a piece of code that catches synchronous and asynchronous errors in try/catch and contract, respectively, without affecting the normal logic of other (subsequent) code
- Write a piece of code that performs a series of asynchronous tasks: ① without a contract, creating “callback hell” ② with a contract chain to solve the problem
- What do promise.all () and promise.race () mean? What terms does it return in different cases?
- Write a piece of code, concatenate multiple contracts, simplify it with reduce(), and encapsulate it as a generic method
- Write a code to implement the following functions: there are two buttons [Start] and [End] on the page, click [Start] to instantiate a new term and print “Start”, one second later, the term is fulfilled and print “success”, click [End] to cancel the term and print “End”.