from ES6 It started to increase Promise type , Called the dominant asynchronous programming mechanism .
time limit Promise It's a state object , It may be in one of the following three states :
- undetermined (pending)
- cash (fulfilled, Or called “ solve ”,resolved)
- Refuse (rejected)
pending It is the initial state of the contract . In this state ,promise Can be done Settled (settled) by fufilled or rejected, They represent success and Failure The state of . No matter which state is settled, it is irreversible , Convert to resolve or reject after , The status of the contract will not change .
promise The status changes to cash resolve when , There will be a private interior value (value), And convert to failure reject when , There will be a private interior reason (reason), The default value is undefined.
The state of being private is due to , Therefore, changing its state can only be operated internally . The control period state transition is realized by calling its two function parameters :resolve() and reject()
In order to avoid the expectation card in the timeout state , You can add a timed exit function . such as , Can pass setTImeout Set up a 10 After seconds, the scheduled callback will be rejected anyway :
1 let p = new Promise((resolve,reject)=>{ 2 setTimeout(reject,10000);//10 Seconds later reject() 3 });
Because the status code of the appointment can only be changed once , So the timeout rejection logic here can be safely written , Because if the state is changed before the timeout execution , Because of its characteristics , Timeout cannot change state .
promise.resolve()
The appointment does not have to be pending at the beginning , And then through resolve() and reject() Function can be converted to the settled state . By calling Promise.resolve() Method can instantiate a solution period .
1 let p1 = new Promise((resolve,reject)=>resolve()); 2 let p2 = new Promise.resolve();
The examples of the above two covenants are actually the same .
Pass in promise.resolve() The parameters of the method correspond to value Value
setTimeout(console.log,0,Promise.resolve(3)); //Promise <resolved>:3
promise.resolve() If the passed in parameter itself is a period of about , Then it behaves like an empty package . therefore ,promise.resolve() It can be said to be a idempotent Method , Idempotency preserves the state of incoming duration .
let p = new Promise(()=>); setTimeout(console.log,0,p);//Promise<pending> setTimeout(console.log,0,Promise.resolve(p));//Promise<pending> setTimeout(console.log,0,p===Promise.resolve(p));//true Congruence Prove that the theory is correct
ps: The concept of idempotency is as follows
Idempotency Idempotent
Idempotency in Mathematics :f(x)=f(f(x)) , Have the same output
Promise.reject()
And promise.resolve() similar , Will instantiate a rejected contract and throw an asynchronous error ( Can only be captured by a denial handler ).
The following two examples are actually the same
let p1 = new Promise(resolve,reject)=>reject()); let p2 = Promise.reject();
Reasons for refusing the appointment (reason) It's for Promise.reject() The first parameter of . This parameter will also be passed to the subsequent reject handler
Promise.prototype.then()
then() Method is the main method to add handlers for instance duration , Up to two parameters can be received :onResolved and onRejected. These two parameters represent success and Refuse The program to process when in state , These two parameters can be selected at the same time , The dates are success and Refuse The state executes the corresponding code
1 function onResolved(id){ 2 setTimeout(console.log,0,id,'resolved'); 3 } 4 function onRejected(id){ 5 setTimeout(console.log,0,id,'rejected'); 6 } 7 let p1 = new Promise((resolve,reject)=>setTimeout(resolve,3000)); 8 let p2 = new Promise((resolve,reject)=>setTimeout(reject,3000)); 9 p1.then(()=>onResolved('p1'), 10 ()=>onRejected("p1")); 11 p2.then(()=>onResolved('p2'), 12 ()=>onRejected("p2"));
//3000ms after
//p1 resolved
//p2 rejected
As mentioned above , Both handler parameters are optional , And because the contract status can only be changed to Settled settle State once , So these two operations must be mutually exclusive .
Besides , Pass to then() Any arguments of non function type are silently ignored . If you just want to provide onRejected Parameters , Then it's in onResolved Passed in at the location of the parameter undefined. This helps avoid creating redundant objects in memory , The type system of expected function parameters is also an explanation .
// Don't pass on onResolved The specification of the handler p2.then(null,()=>onRejected('p2'))
Promise.prototype.then() Method returns a new instance of the contract :
p2.then(null,()=>onRejected('p2')); let p3 = new Promise(()=>{}); let p4 = p3.then(); setTimeout(console.log,0,p3===p4);//false
This new contract instance is based on onResolved The return value of the handler is built . let me put it another way , The return value of this handler will pass Promise.resolve() Packaging to generate a new period of about . If this handler is not provided , be Promise.resolve() It will pack the value after the previous period . If there is no explicit return statement , be Promise.resolve() Will wrap the default return value undefined.
let p1 = Promise.resolve('foo');
let p2 = p1.then();
// If called then() Do not transfer processing program , Then pass it back as it is
setTimeout(console.log,0,p2)//Promise<resolved> : foo
let p3 = p1.then(()=>undefined);
let p4 = p1.then(()=>{});
let p5 = p1.then(()=>Promise.resolve());
setTimeout(console.log,0,p3)//Promise <resolved>:undefined
setTimeout(console.log,0,p4)//Promise <resolved>:undefined
setTimeout(console.log,0,p5)//Promise <resolved>:undefinedIf there is an explicit return value , be Promise.resolve() Will wrap this value :
1 let p6 = p1.then(()=>'bar'); 2 let p7 = p1.then(()=>Promise.resolve('bar')); 3 4 setTimeout(console.log,0,p6)//Promise <resolved>:bar 5 setTimeout(console.log,0,p7)//Promise <resolved>:bar 6 7 // Promise.resolve() Keep the return period about 8 let p8 = p1.then(()=>new Promise(()=>{})); 9 let p9 = p1.then(()=>Promise.reject()); 10 11 12 setTimeout(console.log,0,p8)//Promise <pending> 13 setTimeout(console.log,0,p9)//Promise <rejected>:undefined
Throwing an exception will return the rejected date :
1 let p10 = p1.then(()=>{throw 'baz';}); 2 //Uncaught (in promise) baz 3 setTimeout(console.log,0,p10)//Promise <rejected> baz
Returning an error value will not trigger the above rejection behavior , Instead, the wrong object will be packaged in a resolution period :
let p11 = p1.then(()=>Error('qux'));
setTimeout(console.log,0,p11)//Promise <resolved>:Error quxonRejected The handler is similar :onRejected The value returned by the handler will also be Promise.resolve() packing . The rejection handler does not throw an exception after catching an error, which is a behavior in line with the contract , A settlement period should be returned about .
Promise.prototype.catch()
This method is used to add a reject handler to the appointment . This method only takes one parameter :onRejected The handler . in fact , This method is a Grammatical sugar , Calling it is equivalent to calling Promise.prototype.then(null,onRejected)
Grammatical sugar (Syntactic sugar), Sugar coated grammar , It's Peter, a British computer scientist · John · Randa (Peter J. Landin) A term for invention , A grammar added to a computer language , This grammar has no effect on the function of language , But it's easier for programmers to use . Generally speaking, the use of syntactic sugar can increase the readability of programs , So as to reduce the chance of program code error .
1 let p = Promise.reject(); 2 let onRejected = function(e){ 3 setTimeout(console.log,0,'rejected'); 4 }; 5 6 // The following two ways to add a rejection handler are the same 7 p.then(null,onRejected);//rejected 8 p.catch(onRejected);//rejected
promise.prototype.catch() Returns a new appointment instance :
let p1 = new Promise(()=>{}) let p2 = p1.catch(); setTimeout(console.log,0,p1) setTimeout(console.log,0,p2) setTimeout(console.log,0,p1===p2)
In terms of returning new appointment instances ,Promise.prototype.catch() The behavior and Promise.prototype.then() Of onRejected The handler is the same .
Promise.prototype.finally()
Promise.prototype.finally() Method is used to add to the appointment onFinnally The handler , This handler will be executed when the period changes to the resolved or rejected state . This method can avoid onResolved and onRejected Redundant code in handler . but onFinnally The handler has no way to know whether the status of the appointment is resolved or rejected , So this method is mainly used to add cleaning code .
1 let p1 = Promise.resolve(); 2 let p2 = Promise.reject(); 3 let onFinally=function(){ 4 setTimeout(console.log,0,'Finally!') 5 } 6 7 p1.finally(onFinally)//Finally 8 p2.finally(onFinally)//Finally
promise.prototype.finally() Method returns a new appointment instance :
1 let p1 = new Promise(()=>{}) 2 let p2 = p1.finally(); 3 setTimeout(console.log,0,p1) //Promise <pending> 4 setTimeout(console.log,0,p2) //Promise <pending> 5 setTimeout(console.log,0,p1===p2) //false
This new period is about different from then() or catch() The instance returned by the . because onFinnally Designed as a state independent method , So in most cases, it will be expressed as the transmission of the paternal covenant . This is true for both resolved and rejected States .
Most of the time , Will be passed on as is . But if you return a pending date , perhaps onFinnally The handler threw an error ( A rejection period is explicitly thrown or returned ), The corresponding period will be returned ( Pending or rejected ), As shown below :
1 let p1 = Promise.resolve('foo') 2 let p2 = p1.finally(()=>new Promise(()=>{})) 3 let p3 = p1.finally(()=>Promise.reject()) 4 5 setTimeout(console.log,0,p2)// promise <pending> 6 setTimeout(console.log,0,p3)// promise <rejected>
It is not common to return to a pending appointment , This is because as long as we make an appointment , The new contract will still be passed on to the original contract
as follows :
1 let p1 = Promise.resolve('foo') 2 let p2 = p1.finally(()=>new Promise((resolve,reject)=>setTimeout(()=>resolve('bar'),100))); 3 4 setTimeout(console.log,0,p2) 5 6 setTimeout(()=>setTimeout(console.log,0,p2),200)
The code will output promise<pending> And in the 200ms Post output promise<resolved>:foo, It is obvious that the state of the new instance created by this method is transmitted from the previous instance state .
Non rescheduling method
The current period is about to enter the settlement settle In the state of , The handlers associated with this state will only be scheduled , Not immediately . The synchronization code after adding the code of this handler must be executed before the handler . This feature is made up of JavaScript Runtime guarantee , go by the name of “ Non reentry (non-reentrancy)” characteristic . The following example demonstrates this feature :
1 let p = Promise.resolve() 2 3 p.then(()=>console.log('onResolved handler')) 4 5 console.log('then()returns')
In this case , Call on a resolution period then() Will be able to onResolved The handler advances the message queue . But this handler will not be executed until the synchronous code on the current thread is executed . therefore , With the then() The following synchronization code must be executed before the handler .
It is the same to add a handler first and then solve the problem . The following example shows that even if you add onResolved The handler , Resynchronization call resolve(), The handler will not enter the synchronous thread for execution :
1 let syn; 2 let p = new Promise((resolve) => { 3 syn = function () { 4 console.log('1:invoking resolve()') 5 resolve() 6 console.log('2:resolve() returns') 7 } 8 }) 9 p.then(()=>{ 10 console.log('4:then() handler executes') 11 }) 12 syn() 13 console.log('3:syn() returns');
// 1:...
// 2:...
// 3:...
// 4:...
In this case , The handler acts as an asynchronous task , After the synchronization task is completed , Then from the message queue Out of line execution .
Non reentrant applies to onResolved/onRejected The handler 、catch() Handlers and finnally() The handler .
The following example can be seen clearly : Asynchronous tasks are always executed after the execution of synchronous tasks
1 let p1 = Promise.resolve() 2 p1.then(()=>{console.log('p1.then()')}) 3 console.log('p1 over') 4 5 let p2 = Promise.reject() 6 p2.then(null,()=>console.log("p2.then()")) 7 console.log('p2 over') 8 9 let p3 = Promise.reject() 10 p3.catch(()=>console.log("p3.catch()")) 11 console.log('p3 over') 12 13 let p4 = Promise.resolve() 14 p4.finally(()=>console.log('p4.finnally()')) 15 console.log('p4 over') 16 17 //p1 over 18 //p2 over 19 //p3 over 20 //p4 over 21 // p1.then() 22 // p2.then() 23 // p3.catch() 24 // p4.finnally()
Execution order of adjacent handlers
If you add more than one handler to the appointment , When the current state changes , The relevant handlers will be executed in the order they are added . Whether it's then()、catch()、finally() It's all the same
1 let p1 = Promise.resolve(); 2 let p2 = Promise.reject(); 3 4 p1.then(()=>setTimeout(console.log,0,1)); 5 p1.then(()=>setTimeout(console.log,0,2)); 6 //1 7 //2 8 9 p2.then(null,()=>setTimeout(console.log,0,3)) 10 p2.then(null,()=>setTimeout(console.log,0,4)) 11 //3 12 //4 13 14 p2.catch(()=>setTimeout(console.log,0,5)) 15 p2.catch(()=>setTimeout(console.log,0,6)) 16 //5 17 //6 18 19 p1.finally(()=>setTimeout(console.log,0,7)) 20 p1.finally(()=>setTimeout(console.log,0,8)) 21 //7 22 //8
Pass the resolution value and the reason for rejection
It's settled settle Post state , Date offers its Solution value value( If it works ) or Reasons for rejection reason( If you fail ) To the relevant state handler . After getting the return value , You can further manipulate this value . such as , The first network request returns JSON Is the data necessary to send the second request , Then the value returned from the first request should be passed to onResolved The handler continues processing . Of course , Failed network requests should also send HTTP The status code is sent to onRejected The handler .
In the process of executing , The value of the solution and the reason for rejection are taken as resolve() and reject() The first parameter of is passed back . then , These values will To their respective handlers , As onResolved or onRejected Parameters of . The following example shows the delivery process :
1 let p1 = new Promise((resolve,reject)=>resolve('foo')) 2 // here promise example p1 Called in resolve Method And passed the parameters 'foo' To the corresponding state handler -- As onResolved Parameters of 3 p1.then((value)=>{console.log(value)}) 4 //foo 5 6 let p2 = new Promise((resolve,reject)=>reject('baz')) 7 // ditto 8 p2.catch((reason)=>console.log(reason)) 9 //baz
Of course promise.resolve() and promise.reject() The same way
Rejection period and rejection error handling
The rejection period is about similar to throw() expression , Because they all represent a program state , That is, interrupt or special processing is required . Throwing an error in the expected execution function or handler will result in rejection , The corresponding error object will Become The reason for rejection . Therefore, the following appointments will be rejected on the grounds of a wrong object :
1 let p1 = new Promise((resolve,reject)=>reject(Error('foo'))); 2 let p2 = new Promise((resolve,reject)=>{throw Error('foo');}); 3 let p3 = Promise.resolve().then(()=>{throw Error('foo');}); 4 let p4 = Promise.reject(Error('foo')); 5 6 setTimeout(console.log,0,p1) //Promise <rejected>:Error :foo 7 setTimeout(console.log,0,p2) //Promise <rejected>:Error :foo 8 setTimeout(console.log,0,p3) //Promise <rejected>:Error :foo 9 setTimeout(console.log,0,p4) //Promise <rejected>:Error :foo
A contract can be refused for any reason , Include undefined, But it's best to use the wrong objects uniformly . This is mainly because creating an error object allows the browser to capture stack tracking information in the error object , And this information is very critical for debugging ! For example, the stack tracking information of the first error thrown in the previous case :
1 Uncaught (in promise) Error: foo 2 at 04.html:112:55 3 at new Promise (<anonymous>) 4 at 04.html:112:18
All errors are thrown asynchronously and unhandled , The stack trace information captured by the error object shows the path where the error occurred . Pay attention to the wrong order :Promise.resolve().then() Your mistake only came out last , This is because it requires adding handlers to the runtime message queue ; in other words , It will also Create another appointment .
This example also reveals a side effect of asynchronous errors : Under normal circumstances , Through throw() When keyword throws an error ,throw() The following code will not execute ,JavaScript The error handling mechanism of will run after throw() Pause execution after , But because the asynchronous error is thrown asynchronously from the message queue , The execution of synchronization code will not be suspended at this time , So the subsequent code will continue to be executed . as follows :
1 throw Error('foo') 2 console.log('bar') // This line will not execute 3 4 Promise.reject(Error('foo')) 5 console.log('continue') //continue Successfully printed It means that the synchronization task cannot be suspended
Asynchronous errors can only be made through asynchronous onRejected Handler capture :
1 Promise.reject(Error('foo')).catch((e)=>{}); 2 3 // Enter it on the console to determine whether the capture is correct 4 Promise.reject(Error('foo')).catch((e)=>{console.log(e)});//Error:foo
then() and catch() Of onRejected Handlers are semantically equivalent to try/catch. The starting point is to isolate errors after capturing them , At the same time, it does not affect the normal logic execution . So ,onRejected The handler task should return a solve fufilled Term of contract . The following example compares synchronous error handling with asynchronous error handling :
1 let a = new Promise((resolve,reject)=>{ 2 console.log('begin') 3 reject(Error('bar')) 4 }).catch((e)=>{ 5 console.log('caught error',e) 6 }).then(()=>{ 7 console.log('continue exe') 8 }) 9 console.log(a)// Output here a obtain promise<fufilled> prove onRejected After the handler task catches the asynchronous error Returned a fufilled Term of contract ( Represents the successful capture of exceptions ?)
Periodic linkage and periodic synthesis
Multiple dates can be combined to form a powerful code logic . This combination can be achieved in two ways : Periodic linkage and periodic synthesis . The former is to splice one period after another , The latter is to combine multiple periods into one period .
Date chain
Concatenating dates one by one is a very useful programming model . The reason why this can be done , It is because of the method of each appointment instance (then()、catch()、finally()) Will return to one New date object , And this new period is about It also has its own instance method . In this way, concatenated method calls can form the so-called “ Date chain ”. for example :
1 let p = new Promise((resolve, reject) => { 2 console.log('first') 3 resolve() 4 }) 5 p.then(() => console.log('second')) 6 .then(() => console.log('third')) 7 .then(() => console.log('fourth')) 8 9 //first 10 //second 11 //third 12 //fourth
This implementation finally implemented a series of Synchronization task . therefore , In fact, this way of execution is not so useful ... You can also use multiple synchronization functions directly
If you want to really implement Asynchronous task , You can rewrite the previous example , Let each actuator return a session instance . In this way, we can Let each subsequent appointment wait for the previous appointment , That is, serialize asynchronous tasks . such as , You can solve each issue after a certain time like the following :
1 let p = new Promise((resolve, reject) => { 2 console.log('first') 3 setTimeout(resolve, 1000); 4 }); 5 p.then(() => new Promise((resolve, reject) => { 6 console.log('second') 7 setTimeout(resolve, 1000); 8 })) 9 .then(() => new Promise((resolve, reject) => { 10 console.log('third') 11 setTimeout(resolve, 1000); 12 })) 13 .then(() => new Promise((resolve, reject) => { 14 console.log('fourth') 15 setTimeout(resolve,1000) 16 }))
Extract the generated code into a factory pattern function , as follows :
1 function delay(str){ 2 return new Promise((resolve,reject)=>{ 3 console.log(str); 4 setTimeout(resolve,1000); 5 }) 6 } 7 delay('p1 exe') 8 .then(()=>delay('p2 exe')) 9 .then(()=>delay('p3 exe')) 10 .then(()=>delay('p4 exe'))
Each subsequent handler will wait for the previous appointment to be resolved , Then instantiate a new contract and return it . This structure can simply serialize a task , Solve the problem of relying on callback before . In this case, the service life is about , Then the previous code may be written like this :
1 function delay(str,callback=null){ 2 setTimeout(()=>{ 3 console.log(str) 4 callback && callback(); 5 },1000) 6 } 7 8 delay('p1 callback',()=>{ 9 delay('p2 callback',()=>{ 10 delay('p3 callback',()=>{ 11 delay('p4 callback') 12 }) 13 }) 14 })
This is exactly what the contract aims to solve Back to hell .
Date map
Because a period can have any number of handlers , So futures chain can be built Directed acyclic graph Structure . such , Each period is a node in the graph , The handler added with instance method is directed vertex . Because each node in the graph will wait for the previous node to settle , So the direction of the graph is the order of resolution or rejection of the contract .
The following example shows a periodic digraph , That is to say Binary tree :
1 let a=new Promise((resolve,reject)=>{ 2 console.log('a'); 3 resolve(); 4 }); 5 6 let b = a.then(()=>console.log('b')) 7 let c = a.then(()=>console.log('c')) 8 9 let d = b.then(()=>console.log('d')) 10 let e = b.then(()=>console.log('e')) 11 let f = c.then(()=>console.log('f')) 12 let g = c.then(()=>console.log('g')) 13 //a 14 //b 15 //c 16 //d 17 //e 18 //f 19 //g
As mentioned earlier , The appointment handlers are executed in the order they are added . Because the processing procedure of the appointment is First Add to message queue , then Only one by one , Therefore, sequence traversal is formed .
Number is only a form of periodic graph . Considering that the root node is not necessarily unique , And multiple covenants can also be combined into one covenant ( adopt Promise.all() and Promise.race()), Therefore, directed acyclic graph is the most accurate expression to reflect the possibility of futures chain .
Promise.all() and Promise.race()
Promise Class provides two static methods that combine multiple contract instances into one contract :Promise.all() and Promise.race(). And the behavior of the covenant in the later stage of synthesis depends on the behavior of the internal covenant
Promise.all()
Promise.all() The appointment created by the static method will be solved after a group of appointments are all solved . This static method receives a Iteratable object , Return to a new term :
1 let p1 = Promise.all([ 2 Promise.resolve(), 3 Promise.resolve() 4 ]); 5 6 // Elements in an iteratable object pass through Promise.resolve() The conversion period is about 7 let p2 = Promise.all([3,4]) 8 9 // An empty iteration object is equivalent to Promise.resolve() 10 let p3 = Promise.all([]) 11 12 // Invalid Report errors TypeError: cannot read Symbol.iterator of undefined 13 let p4 = Promise.all()
Synthetic terms will only be resolved after each contained term is resolved :
1 let p = Promise.all([ 2 Promise.resolve(), 3 new Promise((resolve,reject)=>setTimeout(resolve,1000)) 4 ]) 5 setTimeout(console.log,0,p)//Promise <pending> 6 7 p.then(()=>setTimeout(console.log,0,'all() resolved')) 8 //1s all() resolved
If at least one of the included periods is to be determined , The period of synthesis will also be determined . If there is an inclusive contract rejected , Then the synthetic contract will also be rejected :
1 let p1 = Promise.all([new Promise(()=>{})]) 2 setTimeout(console.log,0,p1)//promise <pending> 3 4 let p2 = Promise.all([ 5 Promise.resolve(), 6 Promise.reject(), 7 Promise.resolve 8 ]) 9 setTimeout(console.log,0,p2)//promise <rejected>
If all appointments are successfully resolved , Then the solution value of the composite period is all the arrays containing the solution value of the period , In the order of iterators :
1 let p = Promise.all([ 2 Promise.resolve(0), 3 Promise.resolve(1), 4 Promise.resolve(2) 5 ]) 6 p.then((values)=>setTimeout(console.log,0,values))
If there is a deadline to refuse , The first rejected date takes its own reason as the rejection reason of the composite date . The subsequent rejection of the contract will not affect the reason for rejection of the final contract . however , This does not affect all normal reject operations for inclusion periods . Synthetic date Silent processing All rejections including appointments , As shown below :
1 let p = Promise.all([ 2 Promise.reject(1), 3 Promise.reject(2), 4 Promise.reject(3) 5 ]) 6 p.catch((reason)=>setTimeout(console.log,0,reason));//1
Promise.race()
Promise.race() The static method returns a packing period of about , Is the mirror image of the first resolved or rejected contract in a set . This method receives an iteratable object , Return to a new term :
1 let p1 = Promise.race([ 2 Promise.resolve(), 3 Promise.resolve() 4 ]); 5 6 // Elements in an iteratable object pass through Promise.resolve() The conversion period is about 7 let p2 = Promise.race([3,4]) 8 console.log(p2) 9 10 // An empty iteration object is equivalent to Promise.resolve() 11 let p3 = Promise.race([]) 12 13 // Invalid Report errors TypeError: cannot read Symbol.iterator of undefined 14 let p4 = Promise.race() 15
promise.race() There will be no discrimination between settled or rejected contracts . Whether it's solving or rejecting , As long as it's the first one to come to an agreement ,promise.race() The solution value or rejection reason is packaged and a new contract is returned :
1 let p1 = Promise.race([ 2 Promise.resolve(3), 3 new Promise((resolve,reject)=>setTimeout(resolve,1000)) 4 ]) 5 setTimeout(console.log,0,p1)// promise<resolved>
If there is an appointment to refuse , As long as it is the first to settle , It will become a reason to refuse the composition period . The subsequent rejection of the contract will not affect the reason for rejection of the final contract . however , This does not affect all normal reject operations for inclusion periods . And Promise.all() similar , The synthetic appointment silently handles all rejections containing appointments , As shown below :
1 let p1 = Promise.race([ 2 Promise.reject(3), 3 new Promise((resolve, reject) => setTimeout(reject, 1000)) 4 ]) 5 p1.catch((reason)=>setTimeout(console.log,0,reason))//3
Although here, only the first reason for rejection will enter the rejection process , But the rejection of the second appointment will also be dealt with silently , No mistake will run away .
Serial phase synthesis
The asynchronous generation of a value and its transmission to the handler . Based on the subsequent period, it is the basic function of the period to use the return value of the previous period to concatenate the period . This is much like function composition , That is, multiple functions are combined into one function , such as :
1 function addTwo(x){return x+2} 2 function addThree(x){return x+3} 3 function addFive(x){return x+5} 4 5 function addTen(x){ 6 return addFive(addTwo(addThree(x))) 7 } 8 console.log(addTen(7))//17
In this case , Three functions are combined into one function based on a value .
Similarly , Futures can also be synthesized like this , Gradually consume a value , And return a result :
1 function addTwo(x){return x+2} 2 function addThree(x){return x+3} 3 function addFive(x){return x+5} 4 5 function addTen(x){ 6 return Promise.resolve(x) 7 .then(addTwo) 8 .then(addThree) 9 .then(addFive); 10 } 11 12 addTen(8).then(console.log)//18
Use Array.prototype.reduce() It can be written in a more concise form :
1 function addTwo(x){return x+2} 2 function addThree(x){return x+3} 3 function addFive(x){return x+5} 4 function addTen(x){ 5 return [addTwo,addThree,addFive] 6 .reduce((promise,fn)=>promise.then(fn),Promise.resolve(x)) 7 } 8 9 addTen(8).then(console.log)//18
This pattern can extract a general function , Any number of functions can be used as a handler to synthesize a continuous value passing duration chain . This general compositing function can be implemented in this way :
1 function addTwo(x){return x+2} 2 function addThree(x){return x+3} 3 function addFive(x){return x+5} 4 5 function compose(...fns){ 6 return(x)=>fns.reduce((promise,fn)=>promise.then(fn),Promise.resolve(x)) 7 } 8 let addTen = compose(addTwo,addThree,addFive) 9 addTen(8).then(console.log)//18
The period is about to expand
ES6 The realization of the promise is very reliable , But it also has shortcomings . such as , Many third-party contract library implementations have ECMAScript Two features not covered by the specification : Cancellation and progress tracking .
The appointment is cancelled
We often encounter appointments that are being processed , A situation where the program no longer needs its results . At this time, it would be great if we could cancel the appointment . Some third party libraries , such as Bluebird, This feature is provided . actually ,TC39 The committee was also prepared to add this feature , But the proposal was eventually withdrawn . result ,ES6 The appointment is considered “ Radical ”: As long as the logic of the contract starts to execute , There is no way to prevent it from executing to completion .
actually , It can provide a temporary encapsulation based on the existing implementation , To realize the function of canceling the appointment . This can be used KevinSmith Mentioned “ Cancel the token (cancel token)”. The generated token instance provides an interface , Using this interface, you can cancel the appointment ; At the same time, it also provides an example of dating , It can be used to trigger the operation after cancellation and evaluate the cancellation status .
Here is CancelToken A basic instance of class :
1 class CancelToken{ 2 constructor(cancelFn){ 3 this.promise = new Promise((resolve,reject)=>{ 4 cancelFn(resolve); 5 }) 6 } 7 }
This class is packed for a period , Expose the solution to cancelFn Parameters . such , External code can pass a function into the constructor , So as to control under what circumstances the appointment can be cancelled . Here, it is expected to be a public member of the token class , So you can add a handler to it to cancel the appointment .

![[fault diagnosis] bearing fault diagnosis based on Bayesian optimization support vector machine with matlab code](/img/9e/138e4b160fa9bd6486fac44a788d09.png)


![[zeloengine] summary of pit filling of reflection system](/img/7a/c85ba66c5dd05908b2d784fab306a2.png)


![[IJCAI 2022] parameter efficient large model sparse training method, which greatly reduces the resources required for sparse training](/img/d4/bcc577f320a893c7006177993b2e7a.png)

