Concurrency in JavaScript: An example of burger making under asynchronous operations

This article wants to talk about the application of synchronous and asynchronous operations in JavaScript in a simple example. We'll use the example of making a burger and show how to do it using synchronous methods, callbacks, and Promises with async/await.

Let’s imagine we’re trying to make a burger:

1. Get ingredients to get raw materials (such as beef)

2. Cook the beef

3. Get burger buns

4. Put the cooked beef between the buns

5. Serve the burger

Next, we use different methods to demonstrate the implementation of these steps.

1. Synchronization method

First, let's look at an example that implements a hamburger-making process using synchronous methods:

function getBeef() {
    console.log("Step 1: Getting beef");
    return "beef";
}

function cookBeef(beef) {
    console.log("Step 2: Cooking beef");
    if (beef === "beef") 
        return "patty";
}

function getBuns() {
    console.log("Step 3: Getting buns");
    return "buns";
}

function putBeefBetweenBuns(buns, patty){
    if (buns === "buns" && patty === "patty") {
        console.log("Step 4: Putting beef patty between buns");
        return "burger"
    }
}

function makeBurger() {
    const beef = getBeef();
    const patty = cookBeef(beef);
    const buns = getBuns();
    const burger = putBeefBetweenBuns(buns, patty);
    return burger;
}

function serve(burger){
    console.log("Finally: " + burger + " is served!")
}

const burger = makeBurger();
serve(burger);

In this example, we've used synchronous methods to implement the steps involved in making a hamburger. This method is very simple, there is nothing to talk about, but it may be limited when dealing with complex tasks, because it does not support asynchronous operations.

2. Callbacks

Next, let's look at an example of using a callback function to implement the hamburger making process:

function getBeef(cb) {
    setTimeout(() => {
        console.log("Step 1: Getting beef");
        const beef = "beef";
        cb(beef);
    }, 1000);
}

function cookBeef(beef, cb) {
    setTimeout(() => {
        if(beef === "beef"){
            console.log("Step 2: Cooking beef");
            const patty = "patty"
            cb(patty)
        }
    }, 1000)
}

function getBuns(cb) {
    setTimeout(() => {
        console.log("Step 3: Getting buns");
        const buns = "buns";
        cb(buns)
    }, 1000)
}    

function putBeefBetweenBuns(buns, patty, cb) {
  setTimeout(() => {
    if (buns === "buns" && patty === "patty") {
        console.log("Step 4: Putting beef patty between buns");
        const burger = "burger"
        cb(burger)
    }
  }, 1000);
}

//关键部分
function makeBurger(cb) {
    getBeef(function(beef) {
        cookBeef(beef, function(patty)
        getBuns(function(buns) {
            putBeefBetweenBuns(buns, patty, function(burger) {
                cb(burger);
            });
        });
    });
});
}

function serve(burger){
console.log("Finally: Burger is served!")
}

// Make and serve the burger
makeBurger((burger) => {
serve(burger)
})

In order to understand the above example, let's talk about setTimeout  a function first, setTimeoutwhich is a JavaScript function that is used to execute a certain function or code fragment after a specified time delay. setTimeoutIt will not block code execution itself, it will put the provided callback function (code that needs to be delayed) into a queue, and put it into the event loop after the timer ends, waiting for execution. Let me give you a simple example to explain setTimeouthow works:

console.log("Before setTimeout");

setTimeout(() => {
  console.log("Inside setTimeout");
}, 2000);

console.log("After setTimeout");

When executing this code, you will find that the output sequence is as follows:

Before setTimeout
After setTimeout
大约 2 秒后,会出现:Inside setTimeout

In the second burger-making example, we use callback functions to handle asynchronous operations.

A callback function is simply a function that is passed as an argument to another function. When the called function completes its operation, it executes the passed callback function. In this burger making example, we use callback functions for each step's completion notification.

Here is a detailed explanation of the callback function example:

  1. makeBurger()The function is called, which first calls getBeef()the function, passing an anonymous callback function as an argument. This callback function receives one parameter beef.
  2. getBeef()The function performs an asynchronous operation (using setTimeout()a mock), and when the operation is complete, it calls the passed callback function, passed beefas an argument.
  3. The callback function executes and beefcalls the function with the parameter cookBeef(). Also, we cookBeef()pass an anonymous callback function to the function, which receives a parameter patty.
  4. cookBeef()The function performs an asynchronous operation, and when the operation is complete, it calls the passed callback function, pattypassing it as a parameter.
  5. The callback function executes and pattycalls the function with the parameter getBuns(). We getBuns()pass an anonymous callback function to the function, receiving one parameter buns.
  6. getBuns()The function performs an asynchronous operation, and when the operation is complete, it calls the passed callback function, bunspassing it as a parameter.
  7. The callback function executes and calls the function with the bunsand parameters . We pass an anonymous callback function to the function, receiving one parameter .pattyputBeefBetweenBuns()putBeefBetweenBuns()burger
  8. putBeefBetweenBuns()The function performs an asynchronous operation, and when the operation is complete, it calls the passed callback function, burgerpassing it as a parameter.
  9. The callback function executes, burgerpassing the parameter to serve()the function.
  10. serve()The function prints a message that the burger is ready and served.

In the process, we can see that the callback function is executed after each asynchronous operation is completed, and the result is passed to the next operation. This allows us to handle the entire burger making process asynchronously. However, the disadvantage of this approach is that callback functions may lead to too many layers of nesting, resulting in less readable code. Even if you don’t look carefully, you won’t be able to understand it for a long time, so don’t worry if you can’t understand this code. I think JavaScript’s own syntax and readability and comprehension are extremely rubbish. Don’t worry if you don’t understand it. Anyway, we have to To avoid Callback Hell like the one above, the most commonly used method is the following:

3. Promises and async/await

function getBeef() {
    return new Promise((res) => {
        setTimeout(() => {
            console.log("Step 1: Getting beef");
            res("beef");
        }, 1000);
    });
}
  
function cookBeef(beef) {
    return new Promise((res, rej) => {
        setTimeout(() => {
            if (beef === "beef"){
                console.log("Step 2: Cooking beef");
                res("patty");
            }
            else 
                rej("no beef available");
        }, 1000);
    });
}

function getBuns() {
    return new Promise((res) => {
        setTimeout(() => {
            console.log("Step 3: Getting buns");
            res("buns");
        }, 1000);
    });
}
  
function putBeefBetweenBuns(buns, patty) {
    return new Promise((res, rej) => {
        setTimeout(() => {
            if (buns !== "buns") 
                rej("no buns");
            else if (patty !== "patty") 
                rej("no patty");
            else{
                console.log("Step 4: Putting beef patty between buns");
                res("burger");
            }    
        }, 1000);
    });
}
  
//Promise链式调用
getBeef().then(beef => {
    return Promise.all([
        cookBeef(beef),
        getBuns()
    ])
}).then(ingredients => {
    const [patty, buns] = ingredients;
    return putBeefBetweenBuns(buns, patty)
}).then(burger => {
    console.log("Finally: " + burger + " is served!")
})

//async/await
async function makeBurger(){
    const beef = await getBeef();
    const patty = await cookBeef(beef);
    const buns = await getBuns();
    const burger = await putBeefBetweenBuns(buns, patty);
    return burger
}
makeBurger()

In this example, we use Promise chaining (using the then keyword) and async/await to handle asynchronous operations.

1. For Promise chain calls, use .then()the method to link asynchronous operations together. In this example, first call getBeef(), and then .then()process the obtained in beef. Next, we use Promise.all()to execute cookBeef(beef)and getBuns()together, wait for them both to complete, and then process their results. Finally, we put pattyand bunstogether to compose burger. The advantage of this notation is that it allows you to organize asynchronous operations in a much cleaner way. However, when there are many asynchronous operations, this way of writing may lead to .then()too long chains, making the code difficult to read and maintain.

2. For async/await, it allows you to write asynchronous operations in a way that is closer to synchronous code. In this example, we use awaitto wait for each asynchronous operation to complete and assign the result to the corresponding variable. This way, the code looks like it's executing synchronously, but is actually still asynchronous. The advantage of this way of writing is: it makes the code more concise and easier to read. Also, it allows you to handle errors more easily, since you can try-catchcatch exceptions in asynchronous operations directly using the statement.

Both of these writing methods are used to handle asynchronous operations, and their main difference lies in writing style and readability. Promise chaining focuses more on chaining operations together, while async/await focuses more on making code look like it executes synchronously.

Summarize

In this blog, we compare three ways to implement synchronous and asynchronous operations in JavaScript: synchronous methods, callback functions in asynchronous and Promise chaining calls with async/await. Each method has its pros and cons. Synchronous methods are easy to understand, but do not support asynchronous operations; callback functions support asynchronous operations, but are poor in readability; and Promise and async/await both support asynchronous operations and have good readability, so these two writing methods are recommended.

Guess you like

Origin blog.csdn.net/weixin_44492824/article/details/130453366