15

I want to do something like the following:

delay( 2500 )
  .then( function () { console.log( "Step 1 done" ) } )
  .then( delay( 7500 ) )
  .then( function () { console.log( "Step 2 done" ) } );

So implementation of delay has been demonstrated many times before:

function delay( ms ) {
  var deferred = Q.defer();
  setTimeout( deferred.resolve, ms );
  return deferred.promise;
}

But if I run the above in node.js I get:

... delay of 2500ms
Step 1 done
Step 2 done
... delay of ~7500ms

rather than what I expect to see:

... delay of 2500ms
Step 1 done
... delay of 7500ms
Step 2 done

In the examples provided on https://github.com/kriskowal/q/wiki/Examples-Gallery I can't find any examples of synchronous functions (functions that return a value without any callbacks involved) chained with promise functions.

Any ideas how to mix in synchronous actions with asynchronous promises?

I've tried:

function synchronousPromise() {
  var deferred = Q.defer();
  console.log( "Synchronous function call" );
  deferred.resolve();
  return deferred.promise;
}

delay( 2500 )
  .then( function(){synchronousPromise()} )
  .then( function(){delay( 7500 )} )
  .then( function(){synchronousPromise()} );

And this outputs:

... delay of 2500ms
Time now is 2013-06-20
Time now is 2013-06-20
... delay of 7500ms

.. still not what I'm trying to achieve.

Andry
  • 16,172
  • 27
  • 138
  • 246
PP.
  • 10,764
  • 7
  • 45
  • 59
  • 1
    In your first example, have you tried `.then( function() { return delay( 7500 );})` instead of `.then( delay( 7500 ) )`? – Felix Kling Jun 20 '13 at 12:33
  • @FelixKling that works for the first and second examples! Make that your answer and I shall accept. – PP. Jun 20 '13 at 12:39
  • You can now achieve this without having to specify the delay time check my [answer](http://stackoverflow.com/questions/17213297/can-i-make-a-synchronous-promise-in-the-javascript-q-library/33298652#answer-33298652). – Remo H. Jansen Oct 23 '15 at 09:10

3 Answers3

13

If you want to chain the callbacks, you have to return a new promise object from one of the callbacks. In your first example, you write

.then( delay( 7500 ) )

which means you are passing a promise object to .then, not a function. According to the Promise/A+ proposal (which Q follows), all non-function arguments must be ignored. So, basically it's the same as if you just write:

delay( 2500 )
  .then( function () { console.log( "Step 1 done" ) } )
  .then( function () { console.log( "Step 2 done" ) } );

Instead, pass function which calls delay and returns the promise object:

delay( 2500 )
  .then( function () { console.log( "Step 1 done" ); } ) 
  .then( function () { return delay( 7500 ); } )
  .then( function () { console.log( "Step 2 done" ); } );

Now the last callback will only be called once the promise object returned by delay in the second callback is resolved.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
3

Google brought me here while working through a similar problem (but using Kris Kowal's Q), I ended up with a very small framework that lets you do the following:

var chain = [
  doNext(delay, 2500),
  doNext(console.log, "Step 1 done"),
  doNext(delay, 7500),
  doNext(console.log, "Step 2 done")
];

doInOrder(chain);

The framework is just 12 lines, and probably can be adapted for other promise libraries:

var Q = require('q');

function doNext(fn /* , arguments */){
  var args =  Array.prototype.splice.call(arguments, 1);
  return function(prevRetVal){
    // For my needs I didn't need the results from previous fns
    return fn.apply(null, args)
  }
}

function doInOrder(doNexters, init){
  return doNexters.reduce(Q.when, init);
}
forforf
  • 895
  • 7
  • 14
0

If you work with Babel or TypeScript you could use the ES6 Generators:

  'use strict';

  let asyncTask = () =>
    new Promise(resolve => {
      let delay = Math.floor(Math.random() * 1000);

      setTimeout(function () {
        resolve(delay);
      }, delay);
    });

  let makeMeLookSync = fn => {
    let iterator = fn();
    let loop = result => {
      !result.done && result.value.then(res =>
        loop(iterator.next(res)));
    };

    loop(iterator.next());
  };

  makeMeLookSync(function* () {
    let result = yield asyncTask();

    console.log(result);
  });
Remo H. Jansen
  • 23,172
  • 11
  • 70
  • 93
  • This is the exact same code as in https://www.tivix.com/blog/making-promises-in-a-synchronous-manner, which is a very readable article – myrdd Apr 26 '18 at 11:26