1

what I am trying to do might not be possible in node js, but before I give up I want to get other opinions.

I have a thread which is performing the setup of a product. For the sake of argument, let us call it the 'main' thread. I want all errors that may occur during the product set up to be caught by the 'main' thread. If the setup is successful, then I do not want the 'main' thread to finish before I know that the setup has been successful.

One of the steps during the product setup involves creating a CloudFormation stack in the customer's AWS account. The AWS function to do this is createStack. This function merely starts off the process of creating the stack. If you call this function using await, then the process of creating the stack will still be running after you get the return value.

There is another AWS CloudFormation function called describeStacks which I can use to find out how the creation is going. I cannot call this method repeatedly (without using a large interval between calls) as AWS has limits on how often I can call methods.

I would like to be able to call this function once every minute to check on the progress of the stack creation, until I can determine that the setup has either succeeded or failed. But crucially, I also want the 'main' thread to wait until either this success or failure has occurred.

Is this possible in node js? I am new to node js. Do feel free to tell me that I am misusing it and that I am thinking about the problem in the wrong way. I suspect I am.

SOLUTION: Here is the code that solved the problem for me. Thanks to Tomalak

function checkStackCreationProgress(params) {
    return new Promise( (resolve, reject) => {
        const intervalId = setInterval( async () => {
            try {
                const result = await checkProgress(params);

                switch(result.status) {
                    case 'IN_PROGRESS': break; //do nothing
                    case 'COMPLETE': clearInterval(intervalId); resolve(result); break;
                    case 'FAILED': clearInterval(intervalId); reject(result); break;
                }
            }
            catch(error) {
                clearInterval(intervalId);
                reject(error);
            }
        },
        60000);
    })
    .then( () => {
        return;
    })
    .catch(error => {
        throw new SetupFailedError(`Failed to create ...`, error);
    });
}

This function will not return until the setup has successfully completed or failed

sil
  • 433
  • 8
  • 20

1 Answers1

2

If you call this function using await, then the process of creating the stack will still be running after you get the return value.

Don't call it with await, this blocks the thread.

Async functions return Promises, you can chain them and do other things in the meantime.

So instead of

try {
    var result = await createStack({some: params));
    // work with result
} catch (ex) {
    // handle error
}

do

createStack({some: params)).then(function (result) {
    // work with result
}).catch(function (error) {
    // handle error
});

The second form is non-blocking, you can do any kind of work in the meantime. For example:

var checking = setInterval(function () {
    // check/output for the stack status...
}, 60000)

createStack({some: params)).then(function (result) {
    clearInterval(checking);

    // work with result
}).catch(function (error) {
    clearInterval(checking);

    // handle error
});

console.log("Stack is being created, hang on...");
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thank you for replying. I am not convinced that this will do what I want. The function that I need to call in setInterval (describeStacks) also returns a promise, and it is on this promise that I need to check for the ending conditions. Any errors thrown here will not be catchable outside setInterval. Am I wrong about this? – sil Aug 14 '17 at 14:29
  • 1
    Well, `setInterval()` was just a way of outlining that you can do all of this in parallel in a single node thread. Nothing keeps you from working with more promises while `createStack()` runs. I simply assumed that `createStack()` returns the outcome of the operation and `describeStacks()` was merely used to report on the progress, so I kept it simple. – Tomalak Aug 14 '17 at 15:27
  • 1
    There are multiple posts here that explain how to implement "retry until success"-patterns with promises. [Here's one](https://stackoverflow.com/questions/38213668/promise-retry-design-patterns) and [here's another](https://stackoverflow.com/questions/26694467/promises-repeat-operation-until-it-succeeds), you will find more if you look around. Concrete implementations vary a bit by library used, but it can be done and it's not as hard as it might seem at first. – Tomalak Aug 14 '17 at 15:39
  • That first link did it. (Not sure if that means I should accept your answer?) I guess that half of successful googling is knowing which terms to google. – sil Aug 15 '17 at 11:12
  • 1
    That's part of it, yes. :) That's why I gave you the pointers. I have successfully used psychic debugging to guide you to the solution to your issue from a question with no code whatsoever about a framework I have never worked with; I think this constitutes an "accept". – Tomalak Aug 15 '17 at 11:25
  • 1
    @SiLaf You can post your own answer with the code you have used, it might be helpful to somebody in the future. – Tomalak Aug 15 '17 at 11:30