0

I am creating a learning module for my students where I am trying to demonstrate the power of promises vs callbacks. Unfortunately I am coming from a Python background so callback hell is not something I have had to deal with.

The task at hand is I want to convert an example that I used to demonstrate how async can produce unexpected results into one that produces the expected results using callbacks.

function print1() {
    mimicAsync(1);
}

function print2() {
    mimicAsync(2);
}

function print3() {
    mimicAsync(3);
}

print1();
print2();
print3();
<script>
function mimicAsync(num) {
 setTimeout(function() {
   document.getElementById("output").innerHTML += num;
  }, Math.floor(Math.random() * 1000));
}
</script>
<span id="output"></span>

I know how to do this with Promises, but I want to first show how unpleasant it is with callbacks. I came into JavaScript after Promises were introduced so have little experience with callback hell.

Eric Ianni
  • 75
  • 1
  • 7

1 Answers1

2

function print1(cb) {
  mimicAsync(1, cb);
}

function print2(cb) {
  mimicAsync(2, cb);
}

function print3(cb) {
  mimicAsync(3, cb);
}

function print4() {
  mimicSync(4);
}

function print5(cb) {
  mimicAsync(5, cb);
}

print1(function(err1, data1) {
  if (!err1) {
    print2(function(err2, data2) {
      if (!err2) {
        print3(function(err3, data3) {
          if (!err3) {
            print4();
            print5(function(err5, data5) {
              if (!err5) {
                //do next
              }
            })
          }
        })
      }
    })
  }
});
<script>
  function mimicAsync(num, cb) {
    setTimeout(function() {
      document.getElementById("output").innerHTML += num;
      cb(null, 'success'); //return data in callback
    }, Math.floor(Math.random() * 1000));
  }

  function mimicSync(num) {
    document.getElementById("output").innerHTML += num;
  }
</script>
<span id="output"></span>

And here is the code if we remove the callbacks and use Promises:

function print1() {
  return mimicAsync(1);
}

function print2() {
  return mimicAsync(2);
}

function print3() {
  return mimicAsync(3);
}

function print4() {
  mimicSync(4);
}

function print5() {
  return mimicAsync(5);
}

print1().then(data1 => {
    return print2();
  }).then(data2 => {
    return print3();
  }).then(data3 => {
    print4();
    print5();
  }).then(data5 => { /*do next*/ })
  .catch(err => console.log(err));
<script>
  function mimicAsync(num) {
    return new Promise((res, rej) => {
      setTimeout(function() {
        document.getElementById("output").innerHTML += num;
        res('success'); //resolve the promise
        //rej('error) to reject the promise
      }, Math.floor(Math.random() * 1000));
    });
  }

  function mimicSync(num) {
    document.getElementById("output").innerHTML += num;
  }
</script>
<span id="output"></span>

Additionally you can check this link, where the code is converted from callback style code to promises to async/await.

Boney
  • 2,072
  • 3
  • 18
  • 23
  • I think the part that I was struggling with was thinking I could avoid setting callbacks in the mimicAsync. Is it because the print1() functions themselves aren't the async part? – Eric Ianni Apr 30 '19 at 14:07
  • `mimicAsync` is the one you need callback for since that function has an asynchronous operation whose result will be returned(in the callback function `data` parameter) only after the operation is complete. And because `print1`, `print2` and `print3` has to wait on the result of `mimicAsync`, they too have effectively become asynchronous and hence needs callback. I'm editing my answer for more clarity. Adding a `print4` which is synchronous and hence doesn't need a callback. But `print5` is asynchronous and hence needs the callback parameter. – Boney May 01 '19 at 01:50