How to deal with nested callbacks and avoid “callback hell”(翻译)

(Slag slag little monsters translation, Translation unhappy if you can see -> text )

js is a powerful language. Sometimes you have to deal with another callback callback, and this is another callback callback callback.

People describe the image of this model is the callback hell .

It's a bit like this:

firstFunction(args, function() {
  secondFunction(args, function() {
    thirdFunction(args, function() {
      // And so on…
    });
  });
});
复制代码

This is a js callback. When you see this type of nested callbacks, it may make your brain believe, but I do not think this is "hell." The "hell" can be better managed if you know how to deal with it then.

About callback

I assume you know the concept of callback when you read this article in time. If you do not know, read this article , before we continue to go down this article we will explain what is a callback. There, we discuss what the callback is and why to use them in JavaScript.

Callback processing program

This is a hell of a callback four processing program

  1. Write a comment
  2. Split function into smaller functions
  3. Use Promise
  4. Use Async / await

Before we split explain this solution, let us construct a callback hell. Why? Because it really too abstract, when we see the example above:. FirstFunction, secondFunciton and thirdFunction let us construct a callback really make our case more specific.

Construct a callback hell

Let us imagine, now we have to do in order to make a hamburger a hamburger, we need to perform the following steps to achieve:

  1. Obtaining ingredients
  2. Cooking Beef
  3. Get bread Hamburg
  4. The cooked beef put between bread
  5. Supply Hamburg

If these steps are synchronized, you will see a function like this:

const makeBurger = () => {
  const beef = getBeef();
  const patty = cookBeef(beef);
  const buns = getBuns();
  const burger = putBeefBetweenBuns(buns, beef);
  return burger;
};
const burger = makeBurger();
serve(burger);
复制代码

However, in our steps, we can not make your own burger. We must guide assistant production steps of Hamburg. After we instruct assistant, assistant complete the steps we have to wait before beginning the next step.

If we want to perform again in the middle waiting to js, ​​we need to use a callback. To make the burgers, we have to first get beef. We can only cook beef after obtaining beef.

const makeBurger = () => {
  getBeef(function(beef) {
    // We can only cook beef after we get it.
  });
};
复制代码

To cook beef, beef, we need to pass into the cookBeef function. Otherwise we will not have something to cook. So we had to wait beef cooked.

Once cooked beef, we will get bread

const makeBurger = () => {
  getBeef(function(beef) {
    cookBeef(beef, function(cookedBeef) {
      getBuns(function(buns) {
        // Put patty in bun
      });
    });
  });
};
复制代码

After we get bread, we need to put the patties between two loaves of bread. This is where the formation of Hamburg.

const makeBurger = () => {
  getBeef(function(beef) {
    cookBeef(beef, function(cookedBeef) {
      getBuns(function(buns) {
        putBeefBetweenBuns(buns, beef, function(burger) {
            // Serve the burger
        });
      });
    });
  });
};
复制代码

Ultimately, we can offer burgers. But we can not return burger from makeBurger because it is asynchronous. We need to receive a callback Hamburg.

const makeBurger = nextStep => {
  getBeef(function (beef) {
    cookBeef(beef, function (cookedBeef) {
      getBuns(function (buns) {
        putBeefBetweenBuns(buns, beef, function(burger) {
          nextStep(burger)
        })
      })
    })
  })
}
// Make and serve the burger
makeBurger(function (burger) => {
  serve(burger)
})
复制代码

This callback to make an example of very interesting ah

Solution 1: Write comment

This makeBurger callback is simple and easy to understand. We can read. It does not look good but there are some.

If you first read makeBurger this function, you may be thinking, "Why do we need so many callbacks to make burgers? It does not make sense!"

In this case, you need to leave a comment to explain your code.

// Makes a burger
// makeBurger contains four steps:
//   1. Get beef
//   2. Cook the beef
//   3. Get buns for the burger
//   4. Put the cooked beef between the buns
//   5. Serve the burger (from the callback)
// We use callbacks here because each step is asynchronous.
//   We have to wait for the helper to complete the one step
//   before we can start the next step
const makeBurger = nextStep => {
  getBeef(function(beef) {
    cookBeef(beef, function(cookedBeef) {
      getBuns(function(buns) {
        putBeefBetweenBuns(buns, beef, function(burger) {
          nextStep(burger);
        });
      });
    });
  });
};

复制代码

Now, I would not be thinking "WTF", callback hell when you see this, you can understand why use this way to write it.

Solution 2: split into different callback function

Examples of callback hell we have an example of a great split. Let me give you a step by step to split the code, you will understand why I say so.

Take getBeef, our first callback, we had to go to the refrigerator to get the beef. The kitchen has two refrigerators, we need to take the right-hand side of the refrigerator beef.

const getBeef = nextStep => {
  const fridge = leftFright;
  const beef = getBeefFromFridge(fridge);
  nextStep(beef);
};
复制代码

To cook the beef, we need to put beef in the oven, open the oven to 200 degrees, and wait 20 minutes

const cookBeef = (beef, nextStep) => {
  const workInProgress = putBeefinOven(beef);
  setTimeout(function() {
    nextStep(workInProgress);
  }, 1000 * 60 * 20);
};
复制代码

Now imagine if you had to write each step in makeBurger, then this function will be very large.

For specific examples will be split into smaller callback function, you can read this small part in my callback article.

Solution 3: Use Promise

I guess you should know what is Promise. If you do not know, read this one article

Promise to make a callback hell easier to manage rather than nested callbacks above you will see something like this..:

const makeBurger = () => {
  return getBeef()
    .then(beef => cookBeef(beef))
    .then(cookedBeef => getBuns(beef))
    .then(bunsAndBeef => putBeefBetweenBuns(bunsAndBeef));
};
// Make and serve burger
makeBurger().then(burger => serve(burger));
复制代码

If you prefer a single parameter-style promise, you can convert the example above, this

const makeBurger = () => {
  return getBeef()
    .then(cookBeef)
    .then(getBuns)
    .then(putBeefBetweenBuns);
};
// Make and serve burger
makeBurger().then(serve);
复制代码

More readable and management.

But the problem is how to convert callback hell to Promise?

The callback turn into a Promise

In order to turn into a Promise, we need a new Promise for each callback when the callback executed successfully, then use to resolve this Promise to return. Or when the callback fails or throws an error, we will use the reject.

const getBeefPromise = _ => {
  const fridge = leftFright;
  const beef = getBeefFromFridge(fridge);
  return new Promise((resolve, reject) => {
    if (beef) {
      resolve(beef);
    } else {
      reject(new Error(“No more beef!”));
    }
  });
};
const cookBeefPromise = beef => {
  const workInProgress = putBeefinOven(beef);
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(workInProgress);
    }, 1000 * 60 * 20);
  });
};
复制代码

In our practice, you may have to write a callback function. If you use the Node, each containing a callback function then will have the same syntax:

  1. The callback function will be the last argument
  2. The callback function has two parameters. These parameters have the same sequence (error is first, followed by anything that interests you)
// The function that’s defined for you
const functionName = (arg1, arg2, callback) => {
  // Do stuff here
  callback(err, stuff);
};
// How you use the function
functionName(arg1, arg2, (err, stuff) => {
  if (err) {
  console.error(err);
  }
  // Do stuff
});
复制代码

If you already have a callback same syntax, you can use something like ES6 Promisify Denodeify convert or callback to Promise. If you use the Node v8.0 or above, you can use util.promisify.

Three of them can be used. You can choose any library to use. However, between each method has subtle differences. I suggest that you check its documentation for the method.

Solution 4: the function uses asynchronous (async / await)

In order to use asynchronous function, you first need to know two things.

  1. How to convert callback Promise (Three said that the contents of our solutions)
  2. How to use asynchronous functions (if you need help, you can read this article )

Using asynchronous function, you can write makeBurger, like writing the same sync!

const makeBurger = async () => {
  const beef = await getBeef();
  const cookedBeef = await cookBeef(beef);
  const buns = await getBuns();
  const burger = await putBeefBetweenBuns(cookedBeef, buns);
  return burger;
};
// Make and serve burger
makeBurger().then(serve);
复制代码

We can makeBurger be an improvement here. You can get two getBuns and getBeef assistant at the same time. This means that you can use Promise.all syntax, then await.

const makeBurger = async () => {
  const [beef, buns] = await Promise.all(getBeef, getBuns);
  const cookedBeef = await cookBeef(beef);
  const burger = await putBeefBetweenBuns(cookedBeef, buns);
  return burger;
};
// Make and serve burger
makeBurger().then(serve);
复制代码

(Note: You can do the same effect in Promise, but its syntax is not great, unlike the async / await so clear.)

to sum up

Callback hell not what you imagined so terrible. There are four ways to manage callback hell.

  1. Write a comment
  2. Segmentation function smaller functions
  3. Use Promise
  4. Use async / await

Guess you like

Origin blog.csdn.net/weixin_33801856/article/details/91377377