carlolm
carlolm

Reputation: 277

Javascript Promise/Then Example

I wanted to create an example for my understanding of Promise().then(). I've created an operation that takes time and have operations that must be completed synchronously. The code is below:

// Example: long operation that takes time to execute
var longOperation = function(id) {

    var time = 1000 * Math.random();

    console.log('[' + id + '] Operation started for: ' + time + 'ms');

    setTimeout(function(err) {
        console.log('[' + id + '] Completed operation:   ' + time + 'ms');
        var count = id * 10;
    }, 1000 * Math.random());

};

// Create new promise for longOperation
var longOpPromise = function(id) {

    return new Promise(function(resolve, reject) {
        longOperation(id);
        resolve(null);
    });

};

// Create sequencing
var runLongOperation = function(callback) {

    var count1;

    longOpPromise(1).then(function(count) {

        count1 = count;
        console.log('Count 1: ' + count1);

        longOpPromise(2).then(function(count2) {
            console.log('Count 2: ' + count2);
            callback(null, count1, count2);
        });

    });

};

// Run
runLongOperation(function(err, count1, count2) {
    if (err) console.error(err);
    console.log('Count 1 ' + count1 + ' | Count 2: ' + count2);
});

The result of running:

    **** Output ****
    [1] Operation started for: 626.77225866355ms
    [1] Completed operation:   626.77225866355ms

The methods in then() are not executed, nor is the callback. Maybe count is not being resolved.

If I change it to resolve(null), these execute but not in sequence:

    **** Output ****
    [1] Operation started for: 435.5367429088801ms
    Count 1: null // This should come after [1] is completed
    [2] Operation started for: 256.17250707000494ms
    Count 2: null // This should come after [2] is completed
    Count 1 null | Count 2: null // This should come after [2] is completed
    [2] Completed operation:   256.17250707000494ms
    [1] Completed operation:   435.5367429088801ms

Thanks for your help.

Upvotes: 1

Views: 2189

Answers (3)

otajor
otajor

Reputation: 953

One problem is these two lines:

longOperation(id);
resolve(null);

Because longOperation is an asynchronous function, resolve(null) will run BEFORE longOperation has finished running.

I would combine the two first functions to make sure that the promise does not resolve UNTIL the callback in the setTimeout has run:

var longOpPromise = function(id) {

    var time = 1000 * Math.random();

    console.log('[' + id + '] Operation started for: ' + time + 'ms');

    return new Promise(function(resolve, reject) {
        setTimeout(function(err) {
            console.log('[' + id + '] Completed operation:   ' + time + 'ms');
            var count = id * 10;
            resolve() // Now the promise won't resolve until here.
        }, 1000 * Math.random());
    })


};

I would also avoid mixing callbacks and promises like you are doing - except for inbuilt functions like setTimeout, if you are using promises then you should be able to avoid using callbacks.

Upvotes: 1

Quentin
Quentin

Reputation: 943213

You have to call resolve when the asynchronous operation is complete.

With this code:

longOperation(id);
resolve(null);

You are starting the asynchronous operation and then immediately resolving the promise.


If you want to pass the value of count to your then function, then you need to pass it when you call resolve(). At the moment you are passing null.


This version of your code modifies it to pass resolve as a callback and only call it at the right time.

// Example: long operation that takes time to execute
var longOperation = function(id, resolve) {
  var time = 1000 * Math.random();
  console.log('[' + id + '] Operation started for: ' + time + 'ms');

  setTimeout(function(err) {
    console.log('[' + id + '] Completed operation:   ' + time + 'ms');
    var count = id * 10;
    resolve(count);
  }, 1000 * Math.random());
};

// Create new promise for longOperation
var longOpPromise = function(id) {
  return new Promise(function(resolve, reject) {
    longOperation(id, resolve);
  });
};

// Create sequencing
var runLongOperation = function(callback) {
  var count1;
  longOpPromise(1).then(function(count) {
    count1 = count;
    console.log('Count 1: ' + count1);

    longOpPromise(2).then(function(count2) {
      console.log('Count 2: ' + count2);
      callback(null, count1, count2);
    });
  });
};

// Run
runLongOperation(function(err, count1, count2) {
  if (err) console.error(err);
  console.log('Count 1 ' + count1 + ' | Count 2: ' + count2);
});

Upvotes: 2

Davin Tryon
Davin Tryon

Reputation: 67296

I think the main issue here is that your async action (setTimeout) is not involved with your promise chain.

The resolve(null) after longOperation(id) will immediately return null and not wait for the longOperation function to finish its timeout interval.

Something like this will work:

// Example: long operation that takes time to execute
var longOperation = function(id) {

    var time = 1000 * Math.random();

    console.log('[' + id + '] Operation started for: ' + time + 'ms');

    return new Promise(function(resolve) {
        setTimeout(function(err) {
            console.log('[' + id + '] Completed operation:   ' + time + 'ms');
            var count = id * 10;
            resolve(count);
        }, time);
    });

};

// Create new promise for longOperation
var longOpPromise = function(id) {
    return longOperation(id);
};

I moved the new Promise inside of the longOperation and resolve only when the setTimeout callback is handled.

Upvotes: 1

Related Questions