n/callback-return

Require a `return` with every call to a callback

Calling a callback doesn't end the function. Without a return, the code after the call keeps running, and the callback often gets called a second time.

The rule looks at calls to callback, cb and next inside functions. A call is fine if you return it, if a return comes right after it, or if it's the last statement in the function.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"n/callback-return" = "error" # or "warn"

Examples#

Reported:

function loadUser(id, callback) {
  if (!id) {
    callback(new Error("missing id"));
  }
  db.find(id, callback);
}

Not reported:

function loadUser(id, callback) {
  if (!id) {
    return callback(new Error("missing id"));
  }
  db.find(id, callback);
}

function saveUser(user, callback) {
  if (!user.id) {
    callback(new Error("missing id"));
    return;
  }
  callback(null, db.save(user));
}

The rule doesn't follow control flow. A call at the end of an if block is reported even if nothing runs after it:

function loadUser(id, callback) {
  if (!id) {
    callback(new Error("missing id"));
  } else {
    db.find(id, callback);
  }
}

Options#

The option is the list of names that count as callbacks. It replaces the default, ["callback", "cb", "next"]. A name can be a path such as "res.send".

bunfig.toml
[lint.rules]
"n/callback-return" = ["error", ["done", "res.send"]]
function show(req, res) {
  if (!req.user) {
    res.send(401);
  }
  res.render("profile");
}
function show(req, res, next) {
  if (!req.user) {
    next();
  }
  res.render("profile");
}

When not to use it#

The rule goes by names. It reports a function named next that isn't a callback, and misses a callback with another name. Code that uses promises and async functions doesn't need it.