Daniel Gruszczyk
Daniel Gruszczyk

Reputation: 5622

Promises in while(true) loop / waiting for promise to resolve

I want to write a small worker app in node.js.
This app would read from AWS-SQS, process the data in some way, and spit it out to another AWS-SQS.
So far I have written:

while(true){
    readFromQueue()
    .then(transform)
    .then(writeToQueue);
}

function transform(data) {
    console.log("> transforming...");
    //transformation logic
    return data;
}

//TODO: need to remove message from queue after read!
function readFromQueue() {
    // var params = {
    //   QueueUrl: 'STRING_VALUE',
    //   WaitTimeSeconds: 2
    // };

    // return new Promise(function(resolve, reject) {
    //  sqs.receiveMessage(params, function(err, data) {
    //      if (err) reject(err);
    //      else     resolve(data);
    //  });
    // });
    return new Promise(function(resolve, reject) {
        console.log("> reading from queue...");
        resolve({ data : "world" });
    });
}

function writeToQueue(data) {
    // var params = {
    //  MessageBody: data,
    //  QueueUrl: 'STRING_VALUE',
    // };
    // sqs.sendMessage(params, function(err, data) {
    //  if (err) console.log(err, err.stack);
    //  else     console.log(data);
    // });
    console.log("> writing to queue...");
    console.log(">> " + data);
}

As you can see everything is set up for AWS, but when I run it locally for the time being, I would just have some mock stuff inside, until I actually get my transformation logic tested etc...
The problems I have are:

So am I doing something wrong? I can understand that since promises are async, my while loop will go crazy and create thousands of them, so that concerns me... Nevertheless I want to initiate another loop once the previous read->transform->write has been finished. Is there some other pattern I should use here? Or just block and wait for readFromQueue to end...

--EDIT--
It does execute everything if not wrapped in while(true):

readFromQueue()
        .then(transform)
        .then(writeToQueue);

I also understand that since while(true) is being executed, it will essentially block the thread, and therefore the promise is not resolved. So is there a way around it?

Upvotes: 3

Views: 4739

Answers (4)

rfb
rfb

Reputation: 1167

I also ran into a similar issue (see setTimeout issue trying to wait for execution of async).

I've learned the lesson of sync patterns to be avoided in javascript and so i think your code could be rewritten with something similar:

function queueLooping(loopExitConditionPromise) {

    var blockingGuard = { done: false };

    loopExitConditionPromise.then(function () {
        blockingGuard.done = true;
    });

    return loopingSequence;

    var loopingSequence = function() {
        readFromQueue()
            .then(transform)
            .then(writeToQueue);
        if(!blockingGuard.done) 
            loopingSequence();
        else
            return;
    };

    var readFromQueue = function () {
        console.log("> reading from queue...");
        // ...
    }

    var transform = function (data) {
        console.log("> transforming...");
        // ...
    }

    var writeToQueue = function (data) {
        console.log("> writing to queue...");
        // ...
    }
}

Upvotes: 0

Daniel Gruszczyk
Daniel Gruszczyk

Reputation: 5622

After evaluating all answers in here, I tried deasync, which did the job just fine :)
Nevertheless, I decided to go with mido22 answer above, just because I didn't want a plugin that is fiddling with node's event-loop.

Upvotes: 0

Arg0n
Arg0n

Reputation: 8423

Instead of this:

while(true){
    readFromQueue()
    .then(transform)
    .then(writeToQueue);
}

Can't you use a recursive function? Something like this:

function getData(){
    return readFromQueue()
        .then(transform)
        .then(function(data){
            writeToQueue(data);
            getData();
        });
}

getData(); //Just to get it starting.

Upvotes: 0

mido
mido

Reputation: 25034

I am falling back to my setInterval way. I know you said that reading queue has to start immediately after the writing is finished, but 10ms isn't much of delay if you ask me.

function someFunc(){
    if(readingQueue)    return;
    readingQueue = true;
    return readFromQueue()
      .then(transform)
      .then(writeToQueue)
      .catch(someErrorHandler)
      .then(function(){
        readingQueue=false;
      })
} 

var readingQueue = false;
setInterval(someFunc, 10);

Upvotes: 5

Related Questions