回调函数是将一个函数作为参数传递给另一个函数,当外部函数被调用的时候可以执行一些回调函数。

function funcA() {
  console.log('funcA');
}

function funcB(callback) {
  console.log('funcB');
  callback();
}

funcB(funcA);

匿名回调函数

function funcB(function() {
  // ...
})

回调函数的作用

首先使用一个不使用回调函数的例子:

const calc = function(num1, num2, calcType) {
  if (calcType === 'add') {
    return num1 + num2;
  } else if (calcType === 'multiply') {
    return num1 * num2;
  }
};

calc(2, 3, 'add'); // 5
calc(2, 3, 'multiply'); // 6

接下来使用回调函数:

const add = function(a, b) {
  return a + b;
}

const multiply = function(a, b) {
  return a * b;
}

const calc = function(num1, num2, callback) {
  return callback(num1, num2);
}

calc(2, 3, add); // 5
calc(2, 3, multiply); // 6

回调地狱

回调函数的第一个问题就是回调地狱的问题,回调地狱就是回调函数的大量嵌套回调或者链式回调。

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

回调地狱不仅带来阅读代码的困难性,包含了大量的嵌套与缩进和各个交错的执行顺序。同时回调函数的脆弱性,一旦指定了顺序和事件,代码会变得异常复杂,难以维护。

信任问题

回调函数的另一个问题就是信任问题。

如上述例子中:

calc(2, '3', add); // 23

如上结果,并不是我们预期的结果。对于回调函数 add 我们可以改进

function add(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw Error('Bad parameters');
  }

  return a + b;
}

参考链接