Callbacks hells To Promise / async await rescue

·

4 min read

In this article, I would introduce why promise was introduced? What problem wants to be solved when coding in javascript?

I have learned javascript for four to five years, even used it to work for at least three years. However, I still always confused about when to use promise & async/await? Look into the definition, it is the just a asynchronous call [Promise - JavaScript | MDN]. Is it hard to understand? Yes, I think it is not easy.

Callbacks Hells

Let discuss about this topic first. This is not a new thing while writing the javascript. For example, there are few asynchronous task. We want to print in order: task 1 successfully -> task 2 successfully -> task 3 successfully . However, it is obvious the following example not fulfilled our requirements. How to solve it? Callback!!!

function fetchFirstData1(callback) {
  setTimeout(() => {
    callback(null, `task 1 successfully`);
  }, 3000);
}

function fetchFirstData2(callback) {
  setTimeout(() => {
    callback(null, `task 2 successfully`);
  }, 2000);
}

function fetchFirstData3(callback) {
  setTimeout(() => {
    callback(null, `task 3 successfully`);
  }, 1000);
}


// call in this way cannot get results as our expectations
fetchFirstData1(callback);
fetchFirstData2(callback);
fetchFirstData3(callback);

function callback(error, message) {
  if (error) {
    console.warn({ error });
  } else {
    console.log({ message });
  }
}

// {message: 'task 3 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 1 successfully'}

Here is the example to use callback to solve the problem. We need to wrap the next call function into callback function. It looks really ugly and difficult to understand. At his time, we definitely want to ask if any other way could make things better and easier? Definitely, that's why promise comes in. To solve the such callback hells and make async call easily understand.

// this is the correct way to get the order as our expectation

// first
fetchFirstData1(function (err, message) {
  if (err) {
    console.warn({ err });
  } else {
    console.log({ message });

    // second 
    fetchFirstData2(function (err, message) {
      if (err) {
        console.warn({ err });
      } else {
        console.log({ message });

        // third
        fetchFirstData3(function (err, message) {
          if (err) {
            console.warn(err);
          } else {
            console.log({ message });
          }
        });
      }
    });
  }
});

function fetchFirstData1(callback) {
  setTimeout(() => {
    callback(null, `task 1 successfully`);
  }, 3000);
}

function fetchFirstData2(callback) {
  setTimeout(() => {
    callback(null, `task 2 successfully`);
  }, 2000);
}

function fetchFirstData3(callback) {
  setTimeout(() => {
    callback(null, `task 3 successfully`);
  }, 1000);
}

// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}

Promise to rescue the callback hell

This is example fully explanation the benefits to use promise to keep our asyn call to escape the callback hell. Only need to execute the next step in the then(). But, wait, do we have any better to make clear? Yes, Async/Await. If you search at the google, you definitely know it is a sugar syntax for promise. Actually, they did the same thing.

// call first
fetchFirstData1()
  .then(
    (res) => {
      callback(null, res);
      // call second
      return fetchFirstData2();
    },
    (err) => callback(err)
  )
  .then(
    (res) => {
      callback(null, res);
      // call third
      return fetchFirstData3();
    },
    (err) => callback(err)
  )
  .then(
    (res) => {
      callback(null, res);
    },
    (err) => callback(err)
  );


function fetchFirstData1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 1 successfully`);
    }, 3000);
  });
}

function fetchFirstData2() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 2 successfully`);
    }, 2000);
  });
}

function fetchFirstData3() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 3 successfully`);
    }, 1000);
  });
}

function callback(error, message) {
  if (error) {
    console.warn({ error });
  } else {
    console.log({ message });
  }
}

// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}

Advantage of Async / Await

Here, I want to show another way to improve our async calls used in the for loop. This is a nice way for dynamic call async function without write it down one by one.


fetchAll(
  [fetchFirstData1, fetchFirstData2, fetchFirstData3],
  (error, message) => {
    if (error) {
      console.warn({ error });
    } else {
      console.log({ message });
    }
  }
);
// asyns / await function call
async function fetchAll(tasks, callback) {
  for (let task of tasks) {
    try {
      const res = await task();
      callback(null, res);
    } catch (error) {
      callback(error);
    }
  }
}

function fetchFirstData1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 1 successfully`);
    }, 3000);
  });
}

function fetchFirstData2() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 2 successfully`);
    }, 2000);
  });
}

function fetchFirstData3() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`task 3 successfully`);
    }, 1000);
  });
}

// {message: 'task 1 successfully'}
// {message: 'task 2 successfully'}
// {message: 'task 3 successfully'}

Conclusion

Promise could solve the callback hells problem. Try to think about any other scenario could be used by promise without callback hells. But, it can't fully replace the callback. Async/Await is just a sugar syntax for Promise.

Thanks for the reading. Welcome to leave the comments to further discussion.

Reference

JavaScript.info - Introduction: callbacks : https://javascript.info/callbacks

JavaScript.info - Promise : https://javascript.info/promise-basics