Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no-callback-in-promise: call should be allowed if promise does not return #124

Open
gabmontes opened this issue Apr 10, 2018 · 2 comments
Open

Comments

@gabmontes
Copy link

gabmontes commented Apr 10, 2018

Description

As a general rule, callbacks should not be called inside promises and, as a general rule, it is advisable to "nodeify" the promise call to properly work within a callback context.

Having said that, there are some exceptions where it is absolutely reasonable to call callbacks within promises, i.e. when a promise call inside an async handler does not return. Given there is no promise returned, the only safe way to get out of the handler is to call the callback.

Steps to Reproduce

Given i.e. express or restify middleware functions shall call next and don't handle promises (yet?), a middleware shall be written as:

function middleware (req, res, next) {
  doAsyncOp(req, res)
    .then(() => next())
             // ^^^ "Avoid calling back inside of a promise" is shown
    .catch(next)
}

In the case the framework supports promises, as mocha, calling done instead of returning a promise should be a flag:

it('should not be done like this', function (done) {
  return doAsyncTestOp()
    .then(() => done())
    .catch(done)
}

it('should use this pattern instead', function () {
  return doAsyncTestOp()
}

So, the rule should be updated to trigger only when a callback is called and a promise is returned because is semantically incorrect to do both at the same time but it is allowed to do otherwise.

Expected behavior: The rule is triggered even when no promise is returned.

Actual behavior: The rule should only trigger if a callback is called and a promise is returned.

Versions

  • ESLint version: 4.19.0
  • eslint-plugin-promise version: 3.7.0
@marcogrcr
Copy link
Contributor

Hmm, but wouldn't that result in the unintended behavior of potentially invoking the callback twice?

index.js

doAsyncOp()
  .then(() => callback())
  .catch(callback);

function callback() {
  console.log("Callback got called");
  throw new Error();
}

Output:

$ node .
Callback got called
Callback got called

This is why nodeify uses setImmediate/process.nextTick/setTimeout to wrap the callback invocation:

doAsyncOp()
  .then(() => setImmediate(callback))
  .catch(() => setImmediate(callback));

@bwertman-rc
Copy link

This is an issue with the example, not the premise - you could write it like this and it wouldn't get called twice, but the lint issue is still present:

doAsyncOp()
  .then(() => callback(), callback);

function callback() {
  console.log("Callback got called");
  throw new Error();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants