0

This question has been asked many times but I unfortunately fail to understand how to proceed.

I have a scenario in which a variable may, or may not, need to be assigned a value through an asynchronous call.

This is the code:

var continueFlag = someCondition ? true : false;

if(needToDoAjaxCallFirst){
    $.post(someUrl, someData, function(response){
        if(response == 'continue')
            continueFlag = true;
    });
}

/*
If the async call needed to be executed, then this
needs to be executed AFTER the async call is completed:
*/
if(continueFlag){
    doStuff(); //do stuff here, regardless of whether the async call needed to be executed or not
}

So if the async call needs to be run, I need to wait until it has been completed before I proceed to the if(continueFlag){ part.

Normally I would simply place the if(continueFlag){ part inside the async callback function. But the problem in this case is that the code may also need to be executed even if the async call was not.

If I understand correctly, I need to use a Promise to achieve this. However, if this is indeed the case, I fail to understand how exactly.

UPDATE - one approach that I am trying to avoid:

var continueFlag = someCondition ? true : false;

if(needToDoAjaxCallFirst){
    $.post(someUrl, someData, function(response){
        if(response == 'continue')
            continueFlag = true;
            doStuff();
    });
}

/*
If the async call needed to be executed, then this
needs to be executed AFTER the async call is completed:
*/
if(continueFlag){
    doStuff();
}

Many people in the comments below suggest this approach as the optimal solution - and this is indeed the way I implemented it before asking the question.

However, I do not consider this a neat way to solve the problem as in this case the code to be executed in the end is the same, therefore I consider it code duplication and also not logical: It kind of creates a breakpoint where the course of the code from that point onward is split into two separate "threads"/"branches", depending on whether an async call is needed: if async call needed then go this way, else go that way - both ways completely independent from one another, although the code run in both cases is effectively the same, hence the code duplication.

Such an approach, apart from not being logically optimal in my opinion, also adds unnecessary complexity to the code (and therefore its maintenance) as everything that needs to be done from the breakpoint onward needs to be done in minimum two places.

As one picture equals a thousand words, I am also attaching a flow chart of what I tried to describe above, in the hope that it will at least make my trail of thought clear:

enter image description here

pazof
  • 944
  • 1
  • 12
  • 26
  • 2
    Don't use `continue` as a variable name, it's a JS keyword. – Rax Weber Jun 28 '17 at 10:37
  • True that, I have updated the variable name - it was merely for the sake of simplicity of the sample code, but thank you for pointing it out. – pazof Jun 28 '17 at 10:39
  • 2
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – freedomn-m Jun 28 '17 at 10:40
  • Put your `dostuff` in a separate function and then call it from the post callback *and* the else part of the if(needToCall) – freedomn-m Jun 28 '17 at 10:42
  • This is actually the way I have currently implemented it - however, this is code duplication, and also adds unnecessary (in my opinion) complexity to the code. So I am looking for a way to avoid this, if possible. – pazof Jun 28 '17 at 10:45
  • Please post your solution as an answer not as an update to the question. This is for future visitors to understand and to cause less confusion. Thank you. – Bugs Jun 28 '17 at 11:45
  • It is not a solution but rather a workaround that I am currently using to (inefficiently) solve the problem, which though does not answer the question, therefore it cannot be considered an answer and is there to further explain the problem and prompt people to rule out that approach. – pazof Jun 28 '17 at 11:54
  • Well my solution does exactly what you ask, and was posted before your update. Any problem with that? – Jamiec Jun 28 '17 at 12:38
  • True - Still trying to see if it covers all the use cases that way indeed - though it looks like it does exactly that - I will post back when I am sure. – pazof Jun 28 '17 at 14:10

3 Answers3

2

First, you should avoid the use of reserved javascript keywords. Secondly, you can create a common function that runs when the async call is completed like this

var continueVar = someCondition ? true : false;

if(needToDoAjaxCallFirst){
    $.post(someUrl, someData, function(response){
        if(response == 'continue')
            continueVar = true;
            processContinue();
    });
}

/*
If the async call needed to be executed, then this
needs to be executed AFTER the async call is completed:
*/
if(continueVar){
    processContinue();
}

In both cases as you mentioned, the processContinue() will be invoked without waiting for async complete, lets say in a page load. And another case will be after the async call is completed. So, you can add your logic that you want to implement when continueVar is true inside processContinue()

Ankit Agarwal
  • 30,378
  • 5
  • 37
  • 62
  • This is actually the way I have currently implemented it - however, this is code duplication, and also adds unnecessary complexity to the code. So I am looking for a way to avoid this, if possible. – pazof Jun 28 '17 at 10:44
  • How can you say that as a code duplication. This is what you want to achieve, run a common piece of code after async success and on normal flow. So, i have suggested to keep the logic on common function. – Ankit Agarwal Jun 28 '17 at 10:49
  • Even if we don't consider it as code duplication, it definitely adds unnecessary complexity to the code (and therefore its maintenance), which I am trying to avoid. Let's just say I am trying to keep a single logical course (`first figure out whether we should continue, then either continue or not`), not split the course into two separate "branches" (`if async call needed then go this way, else go that way`). In this case, everything that needs to be done from then onwards needs to be in minimum two places. – pazof Jun 28 '17 at 11:00
1

Without Promise you always can write this like this:

var continueFlag = someCondition ? true : false;

if(needToDoAjaxCallFirst){
    $.post(someUrl, someData, function(response){
        if(response == 'continue')
            continueFlag = true;
        if(continueFlag){
           //if async call needed
        }
    });
}
else
{
    if(continueFlag){
           //if there is no need to async call
    }
}
teo van kot
  • 12,350
  • 10
  • 38
  • 70
  • True - this is actually the way I have currently implemented it - but this is code duplication (as the code is the same in both cases), so I am looking for a way to avoid this. – pazof Jun 28 '17 at 10:41
  • Refactor the duplicated code into its own function and call that function from both places. So the only "duplicate" is the method call, not the method code. – freedomn-m Jun 28 '17 at 10:43
  • @freedomn-m totaly agree – teo van kot Jun 28 '17 at 10:44
  • That is still code duplication, and also adds unnecessary complexity to the code - that's why I'm trying to see if there is a neater way. It works, though. – pazof Jun 28 '17 at 10:49
1

Whatever your functionality is, wrap it up in a function that returns a promise. Either resolve in your async callback, or directly as appropriate

function someFunction(){
    return new Promise(function(resolve,reject){          
        if(needToDoAjaxCallFirst){
            $.post(someUrl, someData, function(response){
                if(response == 'continue')
                    resolve();
            });
        }
        else{
           resolve();
        }
    });
}

usage:

someFunction().then(function(){
    // this gets called either straight away, or after the ajax response
});

You may also choose to reject as appropriate, as well as resolve with param(s) if you need to pass something out to the caller.

Jamiec
  • 133,658
  • 13
  • 134
  • 193