ES6 Promise out for a walk first

Instead of talking about complex concepts, let’s use Promise briefly and roughly to get a sense of what it’s like. So the first question is, what is Promise? Is it a class? Objects? An array? Function? Don’t guess, just print it out, console.dir(Promise). It’s as simple as that.

Promise is a constructor with familiar methods all, Reject, and resolve on its own, and equally familiar methods like THEN and catch on its prototype. If you use a Promise new, you can use “then” or “catch” methods. Let’s play with a new one.


       
  1. var p = new Promise( function(resolve, reject){
  2. // Do some asynchronous operations
  3. setTimeout( function(){
  4. console.log( 'Execution completed');
  5. resolve( 'Whatever data');
  6. }, 2000);
  7. });
Copy the code

Promise’s constructor takes one argument, which is a function, and passes two arguments, resolve and reject, which represent the successful and unsuccessful callback of the asynchronous operation. In fact, it is not accurate to describe “success” and “failure”. According to the standard, resolve is the state of the Promise changed to fullfiled, and reject is the state of the Promise changed to rejected. But we can think of it this way at the beginning, and we’ll get into the concepts later. In the above code, we perform an asynchronous operation, setTimeout, and after 2 seconds, print “Execution complete” and call the resolve method. Running the code will print “Execution completed” after 2 seconds. Attention! I’m just new an object, I’m not calling it, and the function we passed in is already executed, so that’s a fine detail to notice. So when we use promises, we usually wrap them in a function and run the function if we need to, like:


       
  1. function runAsync(){
  2. var p = new Promise( function(resolve, reject){
  3. // Do some asynchronous operations
  4. setTimeout( function(){
  5. console.log( 'Execution completed');
  6. resolve( 'Whatever data');
  7. }, 2000);
  8. });
  9. return p;
  10. }
  11. runAsync()
Copy the code

At this point you should have two questions: 1. Is wrapping such a function useful? 2. Resolve (‘ whatever data ‘); Is this made of dry wool? Let’s move on. At the end of our wrapped function, we return a Promise object, that is, we get a Promise object by executing this function. Remember the then and catch methods on Promise objects? This is where the power comes in. Look at the following code:


       
  1. runAsync(a) .then( function(data){
  2. console .log(data);
  3. // You can do some other things with the data
  4. / /...
  5. });
Copy the code

Call the then method directly on the return of runAsync(). The then takes an argument, which is a function, and takes the argument we passed to resolve in runAsync. Running this code will output “execution completed” two seconds later, followed by “whatever data.” The then function is a callback function that can be executed after a runAsync task has finished executing. That’s where Promise comes in. In a nutshell, it’s the ability to separate out the original callback writing and execute the callback as a chain call after the asynchronous operation. You might dismiss it, but is that all a big Promise can do? I wrapped the callback function and passed it to runAsync as well, like this:


       
  1. function runAsync(callback){
  2. setTimeout( function(){
  3. console.log( 'Execution completed');
  4. callback( 'Whatever data');
  5. }, 2000);
  6. }
  7. runAsync( function(data){
  8. console.log(data);
  9. });
Copy the code

The effect is the same. Why bother using Promise. So the question is, what do you do with multiple layers of callbacks? What if callback is also an asynchronous operation that requires a callback function after execution? You can’t define a callback2 and pass it to the callback. The advantage of promises is that you can continue writing the Promise object in the THEN method and return it, and then continue calling the THEN for callback operations.

The use of chain operation

Therefore, on the surface, Promise can only simplify the writing method of layer upon layer callback. In essence, the essence of Promise is “state”, which can be invoked in time by maintaining state and transferring state. It is much simpler and more flexible than passing callback function. So the correct scenario for using promises looks like this:


       
  1. runAsync1()
  2. .then( function(data){
  3. console.log(data);
  4. return runAsync2();
  5. })
  6. .then( function(data){
  7. console.log(data);
  8. return runAsync3();
  9. })
  10. .then( function(data){
  11. console.log(data);
  12. });
Copy the code

This outputs the contents of each asynchronous callback every two seconds, in order, and the data passed to resolve in runAsync2 can be retrieved in the subsequent then method. The running results are as follows:

Guess how runAsync1, runAsync2, and runAsync3 are defined? Yes, here it is:



       
  1. function runAsync1(){
  2. var p = new Promise( function(resolve, reject){
  3. // Do some asynchronous operations
  4. setTimeout( function(){
  5. console.log( 'Asynchronous task 1 completed');
  6. resolve( 'Whatever data 1');
  7. }, 1000);
  8. });
  9. return p;
  10. }
  11. function runAsync2(){
  12. var p = new Promise( function(resolve, reject){
  13. // Do some asynchronous operations
  14. setTimeout( function(){
  15. console.log( 'Asynchronous Task 2 completed');
  16. resolve( 'Any data 2');
  17. }, 2000);
  18. });
  19. return p;
  20. }
  21. function runAsync3(){
  22. var p = new Promise( function(resolve, reject){
  23. // Do some asynchronous operations
  24. setTimeout( function(){
  25. console.log( 'Asynchronous Task 3 completed');
  26. resolve( 'Any data 3');
  27. }, 2000);
  28. });
  29. return p;
  30. }
Copy the code

View Code

 

In the THEN method, you can also return the data directly instead of the Promise object and then receive the data in the later THEN. For example, we can change the above code to look like this:


       
  1. runAsync1()
  2. .then( function(data){
  3. console.log(data);
  4. return runAsync2();
  5. })
  6. .then( function(data){
  7. console.log(data);
  8. return 'Return data directly'; // The data is returned directly
  9. })
  10. .then( function(data){
  11. console.log(data);
  12. });
Copy the code

The output then looks like this:

  

Reject the use of the

At this point, you should have a basic idea of what promises are. So let’s take a look at what else ES6 Promises can do. We’ve just used resolve, not reject, what does it do? In fact, all of our previous examples are ‘execute successfully’ callbacks, with no ‘fail’ yet. Reject sets the Promise state to rejected, so we can catch the ‘fail’ callback in THEN. Look at the code below.


       
  1. function getNumber(){
  2. var p = new Promise( function(resolve, reject){
  3. // Do some asynchronous operations
  4. setTimeout( function(){
  5. var num = Math.ceil( Math.random()* 10); // Generate random numbers from 1 to 10
  6. if(num<= 5) {
  7. resolve(num);
  8. }
  9. else{
  10. reject( 'The numbers are too big');
  11. }
  12. }, 2000);
  13. });
  14. return p;
  15. }
  16. getNumber()
  17. .then(
  18. function(data){
  19. console.log( 'resolved');
  20. console.log(data);
  21. },
  22. function(reason, data){
  23. console.log( 'rejected');
  24. console.log(reason);
  25. }
  26. );
Copy the code

If the number is less than or equal to 5, we consider it a “success” and call resolve to change the state of the Promise. Otherwise we consider it a “failure,” call Reject and pass an argument as the reason for the failure. Run getNumber and pass two arguments in THEN. The then method accepts two arguments, the first for the resolve callback and the second for the reject callback. So we were able to get the data separately from them. Run this code many times and you will get one of two random results:

or

  

The use of the catch

We know that a Promise object has a catch method as well as a then method, so what does that do? This, like the second argument to then, specifies the reject callback, as follows:


       
  1. getNumber(a)
  2. .then( function(data){
  3. console .log( 'resolved');
  4. console .log(data);
  5. })
  6. .catch( function(reason){
  7. console .log( 'rejected');
  8. console .log(reason);
  9. });
Copy the code

The effect is the same as in the second argument to then. However, it also serves another purpose: if an exception is thrown when the resolve callback (the first argument to then above) is executed, it does not trap the JS, but instead goes into the catch method. Take a look at the following code:


       
  1. getNumber(a)
  2. .then( function(data){
  3. console .log( 'resolved');
  4. console .log(data);
  5. console .log(somedata); // SomeData is undefined here
  6. })
  7. .catch( function(reason){
  8. console .log( 'rejected');
  9. console .log(reason);
  10. });
Copy the code

In the resolve callback, we console.log(somedata); The variable someData is undefined. If we don’t use the Promise, the code will run here and we’ll just get an error on the console and stop running. But here, you get something like this:

That is, it is inside the catch method and passes the cause of the error to the reason argument. Even if the code has an error, it will not report an error, which has the same functionality as our try/catch statement.

All the usage of the

Promise’s All method provides the ability to execute asynchronous operations in parallel and not execute callbacks until all asynchronous operations have been executed. We still use the three functions defined above, runAsync1, runAsync2, runAsync3, as shown in the following example:


       
  1. Promise
  2. .all([runAsync1(), runAsync2(), runAsync3()])
  3. .then( function(results){
  4. console.log(results);
  5. });
Copy the code

All, which takes an array of arguments, and the values in it will eventually return a Promise object. Thus, three asynchronous operations that are executed in parallel do not enter the then until they are all executed. So where is the data returned by the three asynchronous operations? It’s all inside then, so all will put the results of all the asynchronous operations into an array and pass it to THEN, which is what results is above. So the output of the above code is:

With ALL, you can perform multiple asynchronous operations in parallel and process all the returned data in a single callback. Isn’t that cool? There is a scene is very suitable for this, some game materials more applications, when opening the web page, pre-load the need to use a variety of resources such as pictures, Flash and various static files. After everything is loaded, we’ll initialize the page.

The use of the race

The effect of the all method is essentially “the slowest runner executes the callback,” as opposed to the “fastest runner executes the callback,” which is the race method, which is the word for race. Race is the same as all. Let’s change the runAsync1 delay to 1 second:


       
  1. Promise
  2. .race([runAsync1(), runAsync2(), runAsync3()])
  3. .then( function(results){
  4. console.log(results);
  5. });
Copy the code

These three asynchronous operations are also executed in parallel. As you can probably guess, one second later runAsync1 is finished executing, and then is executed. The result is this:

Did you guess right? Not quite, is it? RunAsync2 () and runAsync3() do not stop when the callbacks in then start executing and continue. So after another second, output their end sign. What is the use of this race? There are many different scenarios. For example, we can use Race to set a timeout for an asynchronous request and perform the corresponding operation after the timeout. The code is as follows:


       
  1. // Request an image resource
  2. function requestImg(){
  3. var p = new Promise( function(resolve, reject){
  4. var img = new Image();
  5. img.onload = function(){
  6. resolve(img);
  7. }
  8. img.src = 'xxxxxx';
  9. });
  10. return p;
  11. }
  12. // The delay function is used to time the request
  13. function timeout(){
  14. var p = new Promise( function(resolve, reject){
  15. setTimeout( function(){
  16. reject( 'Image request timed out');
  17. }, 5000);
  18. });
  19. return p;
  20. }
  21. Promise
  22. .race([requestImg(), timeout()])
  23. .then( function(results){
  24. console.log(results);
  25. })
  26. .catch( function(reason){
  27. console.log(reason);
  28. });
Copy the code

The requestImg function will asynchronously request an image, and I’ll write the address as “XXXXXX”, so it will definitely fail. The timeout function is an asynchronous operation with a delay of 5 seconds. We put the two functions that return the Promise object into race, and they race. If the image request is successful within 5 seconds, then the normal process is followed. If the image is not returned within 5 seconds, timeout wins and a catch message is displayed indicating that the image request has timed out. The running results are as follows:

 

conclusion

Is that all there is to ES6 Promise? Yeah, that’s pretty much all you can use. I have also seen done, finally, success, fail, etc. What are these? These are not in the Promise standard, but syntactic sugar that we implement ourselves. All asynchronous operations in this article use setTimeout as an example. Ajax is not used to avoid confusion because the first thing many people think of when they talk about Ajax is jquery’s Ajax, and jquery has its own Promise implementation. If you understand the principle, using setTimeout means the same thing as using Ajax. Speaking of jquery, I have to say that the Promise implementation of jquery is rubbish, and the syntax sugar is confusing. I think the reason why Promise is not universal has a lot to do with jquery.