question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Q.fcall not working as expected and/or README misleading

See original GitHub issue

So I’ve just started working with Q on a project and want to convert a simple nested chain of callbacks into a promise chain. Things don’t quite work the way the beginning of the README suggests with its “flatten the pyramid” code example.

Let’s say I have two functions step1 and step2 like in the example:

function step1(callback) {
  callback('v1 value');
}

function step2(v, callback) {
  console.log("v1: " + v);
  callback('v2 value');
}

We can see the expected behavior by doing a simple nested chain:

>   step1(function(v1) {
...     step2(v1, function(v) {
.....       console.log("final value: " + v);
.....     });
...   });
v1: v1 value
final value: v2 value

‘v1 value’ gets printed and then ‘v2 value’.

If I then try to convert to Q in the manner shown in the README, this is the result:

> Q.fcall(step1).
...     then(step2).
...     then(function(v) {
...       console.log("final value: " + v);
...     }, function(err) {
...       console.log("err: " + err.message);
...       console.log(err.stack);
...     }).
...     done();
undefined
> err: undefined is not a function
TypeError: undefined is not a function
    at step1 (repl:2:1)
    at Promise.apply (/Users/dkim-m/projects/map/node_modules/q/q.js:1022:26)
    at Promise.promise.promiseDispatch (/Users/dkim-m/projects/map/node_modules/q/q.js:661:41)
    at /Users/dkim-m/projects/map/node_modules/q/q.js:1273:25
    at flush (/Users/dkim-m/projects/map/node_modules/q/q.js:106:17)
    at process._tickCallback (node.js:415:13)

step1 is getting called here but it doesn’t receive a callback.

Using deferred’s in the fullfillment handlers works:

>   Q.fcall(function() {
...       var def = Q.defer();
...       step1(def.resolve);
...       return def.promise;
...     }).
...     then(function(v) {
...       var def = Q.defer();
...       step2(v, def.resolve);
...       return def.promise;
...     }).
...     then(function(v) {
...       console.log("final value: " + v);
...     }, function(err) {
...       console.log("err: " + err.message);
...       console.log(err.stack);
...     }).
...     done();
undefined
> v1: v1 value
final value: v2 value

But I really don’t want to have write all that code.

The situation with Q.nfcall is a little better.

Let’s say I have node-style versions of step1 and step2:

function nstep1 (callback) {
  callback(null, 'v1 value');
}

function nstep2(v, callback) {
  console.log("v1: " + v);
  callback(null, 'v2 value');
}

And convert in a similar manner:

>     Q.nfcall(nstep1).
...       then(nstep2).
...       then(function(v) {
...         console.log("final value: " + v);
...       }, function(err) {
...         console.log("err: " + err.message);
...         console.log(err.stack);
...       }).
...       done();
undefined
> v1: v1 value
err: undefined is not a function
TypeError: undefined is not a function
    at nstep2 (repl:3:1)
    at _fulfilled (/Users/dkim-m/projects/map/node_modules/q/q.js:703:54)
    at self.promiseDispatch.done (/Users/dkim-m/projects/map/node_modules/q/q.js:732:30)
    at Promise.promise.promiseDispatch (/Users/dkim-m/projects/map/node_modules/q/q.js:669:13)
    at /Users/dkim-m/projects/map/node_modules/q/q.js:495:49
    at flush (/Users/dkim-m/projects/map/node_modules/q/q.js:106:17)
    at process._tickCallback (node.js:415:13)

The behavior is a little bit different this time. We get a little further – nstep gets executed and it’s value propagated to nstep2 – but nstep2 is not receiving a callback. But there’s hope here because we got a little further this time so we try this:

>   Q.nfcall(nstep1).
...     then(function(v) {
...       return Q.nfcall(nstep2, v);
...     }).
...     then(function(v) {
...       console.log("final value: " + v);
...     }, function(err) {
...       console.log("err: " + err.message);
...       console.log(err.stack);
...     }).
...     done();
undefined
> v1: v1 value
final value: v2 value

Success! This isn’t quite as nice as I was hoping for with the “flatten the pyramid” example but it’s a lot better than having to manipulate deffered’s in my then handlers.

So I guess there’s a few (possible) issues here:

  • Q.fcall doesn’t work as expected
  • If my expectation of how Q.fcall should work is wrong, the README is misleading.
  • Q.nfcall comes closer to my expectation, or at least to my ideal, but doesn’t fully get there.
  • If my expectation is wrong and Q.fcall and Q.nfcall are working as expected:
    • Their behavior is inconsistent.
    • It would be really nice if they did work that way.

Issue Analytics

  • State:closed
  • Created 10 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
kriskowalcommented, Jun 30, 2013

I think what might need to be clarified is that this is not a matter of flattening the pyramid with your existing functions. To be adapted to the promise-style, you no longer pass callbacks, but simply return values or promises in any function. You would adapt your step or nstep functions to be pstep functions. You would only need to use defer, resolve, or nf* functions at the boundary between your promise-oriented functions and your non-promise functions. Does that make sense? What this should look like is:

function step1() {
    // returns a promise
}

function step2(step1Value) {
    // returns a promise
}

function report(step2Value) {
    console.log(step2Value);
    // implicitly returns undefined, albeit a promise for undefined
}

Q.fcall(step1)
.then(step2)
.then(report)
.done();
0reactions
danieldkimcommented, Jul 2, 2013

@domenic that works for me though, again, it would be nice if it (or something) did work the way the current text suggests 😃. although I understand that you can’t make the same assumptions about the adapted function that you can when using Q.nfcall so this may not be so straightforward.

btw, what prompted this was I was playing around with the phantomjs node module, specifically the code sample in their README, and I was trying to flatten that pyramid using Q. the phantomjs functions take callbacks – but not node-style callbacks with an error as the first argument – so this mapped directly to the code sample that’s currently at the top of the README and so I tried to follow the example and it didn’t work. so clarifying the README would at least avoid another newbie opening the same issue in github.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Nodejs promise not working when using q library
There is a problem with your 1st log statement. I've modified your code to work properly. var Q=require('q'); var promise = Q. ......
Read more >
kriskowal/q: A promise library for JavaScript - GitHub
A growing examples gallery, showing how Q can be used to make everything better. From XHR to database access to accessing the Flickr...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found