Solve asynchrony problems – promise, async/await

This is the 8th day of my participation in Gwen Challenge

We usually write JS, often just the program can run on the line, but rarely to explore its principle. Not to mention the question of synchronization versus asynchrony. As a result, programs sometimes get stuck for no apparent reason or sometimes they don’t perform in the desired order and we don’t know what to look for. In the following article, I’ll cover synchronization and asynchrony issues and how to solve asynchronous problems with promise, async/await methods.

Before reading this article, it is recommended that you have an understanding of The Event Loop. You can first read my other article to learn about Event loop and microtask macro tasks, so that the async/await in this article will be more friendly.

Let’s begin the explanation of this article.

Single-threaded and asynchronous

What is a single thread

(1) JS is a single-threaded language that can only do one thing at a time

  • The so-called single thread, is can do one thing at the same time, more than one thing is not good, this is single thread.

(2) Browsers and NodeJS already support JS startup processes, such as Web workers

(3) JS and DOM rendering share the same thread, because JS can modify the DOM structure

  • JSYou can modifyDOMStructure, so that they must share the same thread, which is indirectly a matter of necessity.
  • So whenDOMAt the time of renderingJSIt has to be stopped, andJSImplementation processDOMRendering must also stop.

2. Why asynchronous

There is a wait time when the program encounters problems such as network requests or scheduled tasks.

Suppose a timer is set to 10 seconds. If it is placed in a synchronization task, the synchronization task will block code execution and we will wait 10 seconds to see the desired result. The wait time for 1 timer is probably fine, but what if there are 100 timers at this point? We can’t wait 1,000 seconds to see what we want, it’s almost unrealistic.

Then this time needs asynchrony, through asynchrony to let the program does not block the code execution, the flexible execution of the program.

3. Use asynchronous scenarios

(1) Asynchronous requests, such as Ajax image loading

//ajax
console.log('start');
$.get('./data1.json'.function (data1) {
    console.log(data1);
});
console.log('end');
Copy the code

(2) Scheduled tasks, such as setTimeout and setInterval

//setTimeout
console.log(100);
setTimeout(fucntion(){
	console.log(200);           
}, 1000);
console.log(300);
Copy the code
//setInterval
console.log(100);
setInterval(fucntion(){
	console.log(200);           
}, 1000);
console.log(300);
Copy the code

Second, the promise

In the early days of asynchrony, we used callback as a callback function. The form is as follows:

// Get the first data
$.get(url1, (data1) = > {
    console.log(data1);
    
    // Get the second data
    $.get(url2, (data2) = > {
        console.log(data2);
        
        // Get the third data
        $.get(url3, (data3) = > {
            console.log(data3);
            
            // More data can be obtained
        });
    });
});
Copy the code

As can be seen from the above code, in the early days when data was called, it was one layer after another. Callback was called as if in hell, and the data was also called in a very chaotic manner. So, because callback was so hostile to development, promises were born that solved the problem of callback Hell.

Take a look at Promise with a bit of code.

function getData(url){
    return new Promise((resolve, reject) = > {
        $.ajax({
            url,
            success(data){
                resolve(data);
            },
            error(err){ reject(err); }}); }); }const url1 = '/data1.json';
const url2 = '/data2.json';
const url3 = './data3.json';
getData(url1).then(data1= > {
    console.log(data1);
    return getData(url2);
}).then(data2= > {
    console.log(data2);
    return getData(url3);
}).then(data3= > {
    console.log(data3);
}).catch(err= > console.error(err));

Copy the code

As you can see, after using promise, the code is no longer layered on top of each other, but instead uses.then to fetch data, which is pretty nice to write. So what exactly is promise? So let’s go ahead and explain.

1. Three states of promise

  • pending: wait state, in process, no result yet. For example, a network request is being made, or the timer is out of time.
  • fulfilled: Satisfied state, that is, the event has been resolved and succeeded; And when we did that, we pulled backfulfilledIs in that state and is called backthenFunction.
  • rejected: Rejected state, that is, the event has been rejected, that is, failed; And when we did that, we pulled backrejectIs in that state and is called backcatchFunction.

Conclusion:

  • There will only be pending → depressing, or pending → Rejected state, that is, either success or failure;
  • The result is irreversible, whether it is a state of success or a state of failure.

2. The performance and change of the three states

(1) Change of state

Promise mainly has three states: Pending, depressing and Rejected. When a promise in a pending state is returned, then and catch are not fired. When a FULFILLED state is returned, the then callback function is fired. The Catch callback is fired when a rejected state is returned. So how do they change between these states?

1

Let’s start with a piece of code.

const p1 = new Promise((resolved, rejected) = >{});console.log('p1', p1); //pending
Copy the code

In the above code, the console prints the following.

In this code, there is nothing in p1 to execute, so it is always waiting, therefore pending.

2

const p2 = new Promise((resolved, rejected) = > {
    setTimeout(() = > {
        resolved();
    });
});

console.log('p2', p2); //pending starts printing
setTimeout(() = > console.log('p2-setTimeout', p2)); //fulfilled
Copy the code

In the above code, the console prints the following.

In this code, P2 initially prints a pending state because it does not execute into setTimeout. When setTimeout is executed later, the resolved function will be triggered and a fulfilled state promise will be returned.

3) Demo 3

const p3 = new Promise((resolved, rejected) = > {
    setTimeout(() = > {
        rejected();
    });
});

console.log('p3', p3);
setTimeout(() = > console.log('p3-setTimeout', p3)); //rejected
Copy the code

In the above code, the console prints the following.

In this code, p3 initially prints a pending state because it does not execute into setTimeout. When setTimeout is executed, the Rejected function is also triggered, and a Promise with the Rejected state is returned.

After seeing the change of the promise state, I believe that we will have a certain understanding of the three states of promise when they are triggered. So let’s move on to the promise state.

(2) State performance

  • pendingState, will not triggerthencatch
  • fulfilledState, which triggers subsequentthenCallback function.
  • rejectedState, which triggers subsequentcatchCallback function.

So let’s do that.

1

const p1 = Promise.resolve(100); //fulfilled
console.log('p1', p1);
p1.then(data= > {
    console.log('data', data);
}).catch(err= > {
    console.error('err', err);
});
Copy the code

In the above code, the console prints the following.

In this code, P1 calls the resolved callback function in promise, and when it is executed, P1 will be in the fulfilled state. In the fulfilled state, only the.then callback function will be triggered, but not.catch. So you end up printing data 100.

2

const p2 = Promise.reject('404'); //rejected
console.log('p2', p2);
p2.then(data= > {
    console.log('data2', data);
}).catch(err= > {
    console.log('err2', err);
})
Copy the code

In the above code, the console prints the following.

In this code, P2 calls the Reject callback in promise, and when it is executed, P1 is in the Reject state. In the reject state, only the.catch callback is triggered, but not.then, so err2 404 is printed.

With a basic understanding of the three states, let’s take a closer look at the effects of.then and.catch on states.

3. Influence of THEN and Catch on state (important)

  • thenReturn to normalfulfilledIf there is an error, returnrejected
  • catchReturn to normalfulfilledIf there is an error, returnrejected

This is a big pity. Let’s look at the first rule: Then normally return fulfilled, which must be fulfilled if there is a mistake.

1

const p1 = Promise.resolve().then(() = > {
    return 100;
})
console.log('p1', p1); // This will be a big pity
p1.then(() = > {
    console.log('123');
});
Copy the code

In the above code, the console prints the following.

In this code, P1 calls the resolve callback function in promise. When executing this function, P1 normally returns fulfilled, with no error, so 123 is finally printed.

2

const p2 = Promise.resolve().then(() = > {
    throw new Error('then error');
});
console.log('p2', p2); // Rejected triggers the subsequent. Catch callback
p2.then(() = > {
    console.log('456');
}).catch(err= > {
    console.error('err404', err);
});
Copy the code

In the above code, the console prints the following.

Promise in this code, the p2 call the resolve the callback function that is executed at this time, the p2 in the process of execution, throws an Error, so, there is an Error, return to the rejected state, so the final print out err404 Error: Result of then error.

This is a pity. If there is a mistake, you will return rejected.

1) Demo 1 (Be very careful! !).

const p3 = Promise.reject('my error').catch(err= > {
    console.error(err);
});
console.log('p3', p3); // This is a big pity. Trigger subsequent. Then callbacks
p3.then(() = > {
    console.log(100);
});
Copy the code

In the above code, the console prints the following.

In this code, P3 calls the rejected callback function in the promise. In this code, P3 normally returns an Error during the execution process, and this point needs to be careful!! This may seem counterintuitive, but for Promise, when called Resolved or Rejected, as long as it returns normally and does not throw an exception, the promise state will be fulfilled. Therefore, the final state of P3 is a depressing state, and since it is a depressing state, the.then function can be continued.

2

const p4 = Promise.reject('my error').catch(err= > {
    throw new Error('catch err');
});
console.log('p4', p4); // Rejected triggers the. Catch callback function
p4.then(() = > {
    console.log(200);
}).catch(() = > {
    console.log('some err');
});
Copy the code

In the above code, the console prints the following.

In this code, P4 still calls the Reject callback in promise, and when it executes, p4 raises an Error, so it returns rejected state. Subsequent.catch callbacks are then triggered. So we finally print out the result of some Err.

4. Chain calls to then and catch

After learning the above knowledge, we will review it again through a few questions.

The first question:

Promise.resolve().then(() = > {
    console.log(1);
}).catch(() = > {
    console.log(2);
}).then(() = > {
    console.log(3);
});
Copy the code

This problem prints 1 and 3, and because the promise’s resolve function is called, no.catch function is subsequently triggered.

The second question:

Promise.resolve().then(() = > {
    console.log(1);
    throw new Error('error');
}).catch(() = > {
    console.log(2);
}).then(() = > {
    console.log(3);
});
Copy the code

The promise (‘ resolve ‘) function is called, but the promise (‘ Rejected ‘) state is raised, so the. Then function will not be triggered.

The third question:

Promise.resolve().then(() = > {
    console.log(1);
    throw new Error('error');
}).catch(() = > {
    console.log(2);
}).catch(() = > {  // Here is catch
    console.log(3);
});
Copy the code

(1) The promise is rejected, so only the.catch function is triggered. (2) The promise is rejected. (3) The promise is rejected.

Third, async/await

The asynchronous development of modern JS is basically contracted and popularized by async and await. While.then and.catch in promises are pretty neat, async is much cleaner and can implement asynchronous effects by writing synchronous code. What exactly are such magical async and await? Let’s find out!

1. Introduction

Let’s start with an example to show the difference between Promise and async/await. Suppose we now want to load images asynchronously.

(1) If the.then and.catch of promise are used, the code is as follows:

function loadImg(src){
    const picture = new Promise(
        (resolve, reject) = > {
            const img = document.createElement('img');
            img.onload = () = > {
                resolve(img);
            }
            img.onerror = () = > {
                const err = new Error('Image load failed${src}`); reject(err); } img.src = src; })return picture;
}

const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717, 2336175712 & FM = 58';
const url2 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=614367910, 2483275034 & FM = 58';

loadImg(url1).then(img1= > {
    console.log(img1.width);
    return img1; // Common objects
}).then(img1= > {
    console.log(img1.height);
    return loadImg(url2); / / promise
}).then(img2= > {
    console.log(img2.width);
    return img2;
}).then(img2= > {
    console.log(img2.height);
}).catch(ex= > console.error(ex));

Copy the code

(2) If async is used, the code is as follows:

function loadImg(src){
    const picture = new Promise(
        (resolve, reject) = > {
            const img = document.createElement('img');
            img.onload = () = > {
                resolve(img);
            }
            img.onerror = () = > {
                const err = new Error('Image load failed${src}`); reject(err); } img.src = src; })return picture;
}

const url1 = 'https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717, 2336175712 & FM = 58';
const url2 = 'https://dss2.bdstatic.com/8_V1bjqh_Q23odCf/pacific/1990552278.jpg'; ! (async function () {
    // img1
    const img1 = await loadImg(url1);
    console.log(img1.width, img1.height);

    // img2
    const img2 = await loadImg(url2);
    console.log(img2.width, img2.height); }) ();Copy the code

As you can see, the code is much more elegant if you do it the second way. And most importantly, async and await can be implemented with synchronous code. Let’s start with async and await.

Async and await

  • Background: Solve the asynchronous callback problem and prevent callback hellCallback hell
  • PromisePromise then catchIs a chained call, but is also based on a callback function;
  • async/awaitasync/awaitSynchronous grammarTo completely destroy the callback function, is to destroyAn asynchronous callbackThe ultimate weapon.

3. Relationship between async/await and Promise

(1) Async /await and promise are not mutually exclusive, they complement each other.

(2) Perform async function and return a Promise object.

(3) await = ‘promise’ then

(4) the try… Catch catches exceptions instead of the catch of promises.

Let’s go through (2)(3)(4) one by one.

Rule # 1: Execute async and return a Promise object.

async function fn1(){
    return 100; // return promise.resolve(100);
}

const res1 = fn1(); // Perform async and return a Promise object
console.log('res1', res1); / / promise object
res1.then(data= > {
    console.log('data', data); / / 100
});
Copy the code

In the above code, the console prints the following.

As you can see, the first RES1 returns a Promise object, and the state of the promise object is a depressing state at this time, so the subsequent. Then can be called and data 100 can be printed out.

The second rule: await equals then of promise.

! (async function (){
    const p1 = Promise.resolve(300);
    const data = await p1; //await is equivalent to promise then
    console.log('data', data); //data 300}) (); ! (async function () {
    const data1 = await 400; //await Promise.resolve(400)
    console.log('data1', data1); //data1 400}) ();Copy the code

In the above code, the console prints the following.

This will be a pity state. After that, const data = await await in P1, which is equivalent to the then of promise. Moreover, since P1 is a depressing state at this time,. Then can be called, and data 300 will be output. Similarly, in the second code, when await 400, 400 means Promise. Resolved (400), so it is a pity state. Then, the datA1 400 result will be printed.

Here’s another piece of code:

! (async function (){
    const p2 = Promise.reject('err1');
    const res = await p4; //await is equivalent to promise then
    console.log('res', res); / / not print at all}) ();Copy the code

In the above code, the console prints the following.

As you can see, P2 calls the reject callback, so p2 is reject. But since await triggers.then in the promise, res will not be fired, so await will not be done and console.log(‘res’, res) will not be console.log(‘res’, res); Print.

Rule 3: Try… Catch catches exceptions instead of the catch of promises.

! (async function () {
    const p3 = Promise.reject('err1'); / / rejected state
    try{
        const res = await p3;
        console.log(res);
    }catch(ex){
        console.error(ex); / / try... Catch is equivalent to the catch of promise
    }
})();
Copy the code

In the above code, the console prints the following.

Reject (reject); reject (reject); reject (reject); reject (reject); Catch in catch is equivalent to catch in promise, and P3 belongs to the Rejected state. Therefore, the browser catches an exception and reports an error.

The nature of asynchrony

From the above analysis, both promise and async/await are addressing the asynchronous problem. However, the essence of asynchrony is to solve the problem of synchronization, so the essence of asynchrony is:

  • async/awaitIs the ultimate weapon against asynchronous callbacks;
  • JSIt’s single threaded, it needs to be asynchronous, it needs to be based onevent loop
  • async/awaitIs a grammar candy, but this candy is very useful!!

Let’s look at the order of two async/await lines and review async/await lines.

The first question:

async function async1(){
    console.log('async start'); / / 2
    await async2();
    Callback = async; callback = async; callback = async
    // Similar to event loop
    //Promise.resolve().then(() => console.log('async1 end'))
    console.log('async1 end'); / / 5
}

async function async2(){
    console.log('async2'); / / 3
}

console.log('script start');  / / 1
async1();
console.log('script end'); / / 4
Copy the code

In the above code, the console prints the following.

Async1 (); async1(); async1(); async1(); async1(); Can be regarded as the inside of the callback content, namely the asynchronous content, so, to await execution of corresponding async2 () the content of the inside, after placed await behind all of the content to the asynchronous. Continue to execute 4, wait until the completion of 4, the entire synchronous code has been executed, finally, to execute the asynchronous code, and finally output the content of 5.

So let’s do problem two the same way.

The second question:

async function async1(){
    console.log('async1 start'); / / 2
    await async2();

    // The following three lines are the contents of asynchronous callbacks, callback
    console.log('async1 end'); / / 5
    await async3();
    // The next line is the contents of an asynchronous callback, which is nested within an asynchronous callback.
    console.log('async1 end 2'); / / 7
}

async function async2(){
    console.log('async2'); / / 3
}

async function async3(){
    console.log('async3'); / / 6
}

console.log('script start'); / / 1
async1();
console.log('script end'); / / 4
Copy the code

In the above code, the console prints the following.

No more analysis here! You can follow the steps in the first case.

5

And finally, let’s do two more questions to review the async/await we just talked about.

(1) async/await syntax

async function fn(){
	return 100;
}
(async function(){
    const a = fn(); / /?? Promise
    const b = await fn(); / /?? 100}) ();Copy the code
(async function(){
    console.log('start');
    const a = await 100;
    console.log('a', a);
    const b = await Promise.resolve(200);
    console.log('b', b);
    const c = await Promise.reject(300); // Error, no further execution
    console.log('c', c);
    console.log('end'); }) ();// What is printed out after execution?
//start
/ / 100
/ / 200
Copy the code

(2) The order of async/await

async function async1(){
    console.log('async1 start'); / / 2
    await async2();
    //await everything after that as callback content -- microtasks
    console.log('async1 end'); / / 6
}

async function async2(){
    console.log('async2'); / / 3
}

console.log('script start'); / / 1

setTimeout(function(){ // Macro task -- setTimeout
    console.log('setTimeout'); / / 8
}, 0);

// Execute the function immediately
async1();

// When a promise is initialized, the function passed in is executed immediately
new Promise(function(resolve){ // Promise -- microtasks
    console.log('promise1'); / / 4
    resolve();
}).then(function(){ Micro / / task
    console.log('promise2'); / / 7
});

console.log('script end'); / / 5

// The event loop -- call stack is cleared.
// Perform microtasks
// (try to trigger DOM rendering)
// Trigger event loop to execute macro task

Copy the code

Here will not carry on one by one analysis! You can review and understand the previous knowledge points.

Write at the end

The asynchronous js problem and the solution to the problem of asynchronous here! U1s1, Promise and async/await are very important in our daily front-end development. Basically, async/await is used when writing asynchronous code. I have spent 16 hours summarizing the issues of Event loop and promise, async/await. I hope it will be helpful to you.

At the same time, there may be some bad or not easy to understand the place also welcome comments or private message I exchange ~

See you next time!

Pay attention to the public number Monday laboratory, do not regularly share learning dry goods ~

If this article is useful to you, be sure to like it and follow it