Deep understanding of ES6--11.Promise and asynchronous programming

Deep understanding of ES6--11.Promise and asynchronous programming

Original article & experience summary & from school recruitment to factory A all the way to sunshine and vicissitudes

For details, please click www.codercc.com

Main knowledge points: Promise life cycle, basic Promise operations, Promise chain, response to multiple Promises, and integration of Promises

1. Promise basics

What is callback hell?

When using callback functions for event processing, if multiple callback functions are nested, callback hell will appear, for example:

method1(function(err, result) {
	if (err) {
		throw err;
	} 
	method2(function(err, result) {
		if (err) {
			throw err;
		} 
		method3(function(err, result) {
			if (err) {
				throw err;
			} 
			method4(function(err, result) {
				if (err) {
					throw err;
				} 
				method5(result);
			});
		});
	});
});
 

Nesting multiple method calls like this example will create intricate code that can be difficult to understand and debug. When you want to implement more complex functions, there are also problems with the callback function. What if you want two asynchronous operations to run in parallel and alert you when they are both finished? What if you want to start two asynchronous operations at the same time, but only use the first ending result? Using Promise can avoid the situation of callback hell.

Promise can be used as a placeholder to indicate the execution result of an asynchronous operation. The function can return a Promise without subscribing to an event or passing a callback function to the function.

Promise life cycle

Each Promise goes through a short life cycle, initially in a pending state, which means that the asynchronous operation has not yet ended. A pending Promise is also considered unsettled. Once the asynchronous operation ends, the Promise will be considered settled and enter one of two possible states:

  1. Fulfilled : The asynchronous operation of the Promise has successfully ended;
  2. Rejected (rejected) : The asynchronous operation of the Promise did not end successfully. It may be an error or caused by other reasons.

Inside the [[PromiseState]]property is set "pending", "fulfilled"or "Rejected" Promise Promise so you can not determine programmatically Promisein the end what state. But you can use then()methods to perform certain operations when the state of the Promise changes.

  1. then() method

    then()The method exists on all Promises and accepts two parameters. The first parameter is the function to be called when the Promise is completed, and the result data of the asynchronous operation will be passed to this completion function. The second parameter is the function to be called when the Promise is rejected. Similar to the completion function, the rejection function will be passed in any additional data associated with the rejection. The two parameters of the then() method are optional, so you can freely combine the processing functions for monitoring completion and failure;

  2. catch() method

    Promise has a catch() method, which is equivalent to only passing the rejection processing function to the then() method:

     promise.catch(function(err) {
     	// 
     	console.error(err.message);
     });
    // 
     promise.then(null, function(err) {
     	// 
     	console.error(err.message);
     });
     

Create pending Promise

You can use the Promise constructor to create a Promise instance. This constructor receives one parameter: a function called an exciter, which contains two parameters: a resolve()function and a reject()function. resolve()The function is called when the asynchronous task is successfully executed, and the reject()function is called when the asynchronous task fails. E.g:

let promise = new Promise(function(resolve,reject){
	console.log('hi, promise');
	resolve();

});

promise.then(()=>{
	console.log('hi, then');

});

console.log('hi');

 
hi, promise
hi
hi then
 

It can be seen from the output that the code in the Promise constructor is executed first, and the then()code is executed last. This is because only after the execution of the handler function in the Promise ends, the completion processing function in the then() method Or rejection processing function will be added to the end of the job queue.

Create a resolved promise

  1. usePromise.resolve()

Promise.resolve()The method receives a parameter and returns a completed state. The completion processing function can be used Promisein the then()method to extract the Promisepassed value of the completed state , for example:

let promise = Promise.resolve('hi');
promise.then((value)=>{
	console.log(value);//hi
});
 
  1. Use Promise.reject()

You can use Promise.reject()methods to create a rejected state, Promiseand the value passed by the catch()method can only be accepted in the rejection processing function or method reject():

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

reject.catch((value)=>{
	console.log(value);//reject
})
 

Non-promise thenable

When an object has an acceptable resolveand rejectparameters then()when the method, the object is considered to be a non- Promisea thenable, for example:

let thenable = {

	then:function(resolve,reject){
		resolve('hi');
	}
}
 

Promise.resolve()Promise.reject()Both methods and methods can accept non-Promise thenable as a parameter. When a non-Promise thenable is passed in, these methods will create a new Promise, and you can use the then() method to operate on different states:

Create a completed Promise

let thenable = {

	then:function(resolve,reject){
		resolve('hi');
	}
}

let promise = Promise.resolve(thenable);
promise.then((value)=>{
	console.log(value);//hi
});
 

You can also use thenable to create a rejected Promise:

let thenable = {

	then:function(resolve,reject){
		reject('hi');
	}
}

let promise = Promise.resolve(thenable);
promise.then(null,(value)=>{
	console.log(value);
});
 

Actuator error

When an error is thrown inside the executor, the Promise rejection processing function will be called, for example:

let promise = new Promise(function(resolve,reject){
	throw new Error('Error!');

})

promise.catch(function(msg){
	console.log(msg);//error
})
 

2. Promise chain

In addition to using a single Promise, multiple Promises can be used in cascade. In fact, the then()method or catch()method will return a new Promise. Only after the current Promise is resolved, the next Promise will be processed.

Concatenation Promise

let p1 = new Promise(function(resolve,reject){
	resolve('hi');
});

p1.then((value)=>{
	console.log(value);
	throw new Error('Error!');
}).catch(function(error){
	console.log(error);
})
 

It can be seen that when the then()execution of the method of p1 ends, a Promise will be returned, so the catch()method can continue to be executed on this basis . At the same time, the Promise chain allows the error of the previous Promise to be caught .

Passing value in the promise chain

**Another important aspect of the Promise chain is the ability to pass data from one Promise to another. ** The return value of the completion processing function of the previous Promise is passed to the next Promise.

//Promise 

let p1 = new Promise(function(resolve,reject){
	resolve(1);
})

p1.then(value=>value+1)
.then(value=>{
	console.log(value);
})
 

The completion processing function of p1 returns value+1, that is 2, it will be passed to the completion processing function of the next Promise. Therefore, then()the completion processing function in the second method will be output 2. Rejection processing functions can also be used to pass data in the Promise chain.

Passing Promises in the Promise chain

The basic type value can be returned in the completion or rejection processing function, so that it can be passed in the Promise chain. In addition, objects can also be passed in the Promise chain. If the Promise object is passed, additional processing is required:

Promise that delivers the completed state :

let p1 = new Promise(function(resolve,reject){
	resolve(1);
});

let p2 = new Promise(function(resolve,reject){
	resolve(2);
})

p1.then(value=>{
	console.log(value);
	return p2;
}).then(value=>{
	console.log(value);
});
 1  2
 

The Promise object is returned in p1 p2. When it is p2completed, the second then()method is called and the value is valuepassed to the completion processing function. If the Promiseobject p2is rejected, then()the completion processing function in the second method will not be executed, and the value passed by p2 can only be received through the rejection processing function:

let p1 = new Promise(function(resolve,reject){
	resolve(1);
});

let p2 = new Promise(function(resolve,reject){
	reject(2);
})

p1.then(value=>{
	console.log(value);
	return p2;
}).catch(value=>{
	console.log(value);
});
 

3. Respond to multiple Promises

If you want to monitor the status of multiple Promises to determine the next action, you can use the two methods provided by ES6: Promise.all()and Promise.race();

Promise.all()

The Promise.all() method can accept a single iterable object (such as an array) as a parameter, and the elements of the iterable object are all Promises. This method will return a Promise, and the returned Promise will be completed only if all the promises passed in have been completed, for example:

//Promise.all()
let p1 = new Promise(function(resolve,reject){
	resolve(1);
})

let p2 = new Promise(function(resolve,reject){
	resolve(2);
})

let p3 = new Promise(function(resolve,reject){
	resolve(3);
})

let p4 = Promise.all([p1,p2,p3]);
p4.then(value=>{
	console.log(Array.isArray(value));//true
	console.log(value);//[1,2,3]
})
 

For Promise.all()calls to create a new Promise p4, on p1, p2and p3after the completion of all, p4it will eventually also be completed. Passed to the p4result of the completion of the processing function is an array containing for each resolution value (1, 2 and 3), the storage order of the values of the resolution to be maintainedPromise the order (regardless of the order completion), so you can The results match each Promise.

If Promise.all()a Promise passed to is rejected, the Promise returned by the method will be rejected immediately, without waiting for the other Promises to end :

//Promise.all()
let p1 = new Promise(function(resolve,reject){
	resolve(1);
})

let p2 = new Promise(function(resolve,reject){
	reject(2);
})

let p3 = new Promise(function(resolve,reject){
	resolve(3);
})

let p4 = Promise.all([p1,p2,p3]);
p4.catch(value=>{
	console.log(Array.isArray(value));//true
	console.log(value);//2
})
 

In this example, p2 is rejected with the value 2, and the rejection processing function of p4 is called immediately without waiting for p1 or p3 to finish execution (they will still finish execution respectively, but p4 does not wait for them).

The rejection handler will always receive a single value, not an array. This value is the rejection value returned by the rejected Promise.

Promise.race()

Promise.race()The method receives an iterable object whose element is a Promise and returns a new Promise. Once inPromise.race() a Promise in the iterable object is in the resolved state, the returned Promise object will immediately become the resolved state.

The Promise.all()method has to wait until all incoming Promises have become resolved before the returned Promises will be resolved.

let p1 = new Promise(function(resolve,reject){
	resolve(1);
})

let p2 = new Promise(function(resolve,reject){
	resolve(2);
})

let p3 = new Promise(function(resolve,reject){
	resolve(3);
})

let p4 = Promise.race([p1,p2,p3]);
p4.then(value=>{
	console.log(Array.isArray(value));//false
	console.log(value);//1
})
 

In the Promise passed by the Promise.race() method, which Promise becomes the completed state first, the value will be passed to the completion processing function of the returned Promise object. If which Promise becomes the rejected state first, the same will pass the value to p4the rejection processing function.

4. Inherit Promise

You can inherit Promise to implement a custom Promise, for example:

class MyPromise extends Promise {
	// 
	success(resolve, reject) {
		return this.then(resolve, reject);
	} 
	failure(reject) {
		return this.catch(reject);
	}
} 
let promise = new MyPromise(function(resolve, reject) {
	resolve(42);
});
promise.success(function(value) {
	console.log(value);//42
}).failure(function(value) {
	console.log(value);
});
 

In this example, MyPromise is derived from Promise and has two additional methods. success()The method is simulated resolve(), the failure()method is simulatedreject() .

5. Summary

  1. Promise has three states: pending, completed, and rejected. A Promisestarting suspended state, and when the state turned to complete success, failure or rejection turned state. then()The method allows you to bind the rejection completion handler handler, the catch()method only allows you to bind refuse handler;

  2. Multiple Promises can be connected in series to form a Promise chain, and values can be passed in between, and even Promise objects can be passed. The then() call creates and returns a new Promise. Only if the previous Promise has been resolved, the new Promise will also be resolved. At the same time, you can also use the Promise.all() and Promise.race() methods to manage multiple Promises.