Javascript Promises - Procedural Asynchronicity

That topic sounds strange to me, but it is the only way I can describe what I am doing.

So say you have a list of functions that have to run in order, then you have a function or set of code that has to run at the end of the list. Simple you can chain promises together, but what if some of the functions in that list only have to run some of the time.

This is completely possible with Promises! Although it’s not necessarily the intended purpose.
Here is the final code and I will explain in detail further down:

var startPromiseChain = new Promise(function(continue, skip) {
    var initialData = { One : "two", Three : "Four" }
    if(SkipFirstFunction) {
        initialData.Success = true ;
        skip(initialData);	
    } else {
        continue(initialData);
    }
});

startPromiseChain.then(function(data) {
    return FirstFunction(data);
}).then(function(data) {
    if(data.Success) {
       	return SecondFunction(data);
    } else {
	return data;
    }
}).catch(function(data) {
    if(data.Success) {
        return SecondFunction(data);
    } else {
        return data;
    }
}).then(function(data) {
    if(data.Success) {
        console.log("Success!");
    } else {
        console.log("Error: " + data.Msg);
    }
});

Now you may be thinking what in the world is that guy thinking? But its really quite simple!

First off FirstFunction() and SecondFunction() return Promises. This is important!
Lets just say both look similar to this:

function FirstFunction(data) {
    return new Promise(function(continue, skip) {
        /* Do something */
        if(SomethingSuccessful) {
           data.Success = true;
           data.Msg = "Success";
           data.Other = "Data"; //Return some data if need be
        } else {
            /* Error occured */
            data.Success = false;
            data.Msg = "Error Message"; //Return an error
        }
        /* Based on results decide whether or not to skip the next function */
        if(skipNext) {
            skip(data);
        } else {
            continue(data);
        }
    });
}

So instead of resolve and reject we now have continue (go to next function) and skip (skip next function)
Its important that all data returned be consistent, and can tell the next step in the chain whether or not an error has occurred I do this with an object with a “Success” Property and a “Msg” Property. The Msg can be a confirmation or an error. Other data can be passed with this object as well.

So lets start with the initial Promise in the chain the “startPromiseChain”:

/* Use "Continue" and "Skip" to be easier to understand */
var startPromiseChain = new Promise(continue, skip) {
    var initialData = { One : "Two", Three : "Four" }; //This in the data to pass down the chain of functions
    /* Do something to check whether or not the next Function (promise) should be skipped */
    if(SkipNext) {
        /* If the next function is to be skipped call the "Skip (reject)" property */
        initialData.Success = true; //Set success to true so that the chain doesn't think an error occurred
        skip(initialData); //Pass in the initial data
    } else {
        /* If not skipping next function call the "Continue (resolve)" property */
        continue(data); //Pass in the initial data
    }
});

So now we have kicked things off, the starter promise just checks if the first function should be skipped.
If the function should be skipped it calls skip() (or reject()) Property and skips down to the next .catch() block in the chain effectively skipping the .then() block which called FirstFunction()

After this .catch() block is run it continues to the next .then() block. So we have called the SecondFunction() first. But what if we want to run FirstFunction()? Then we call the continue() (or resolve()) Property and the initial .then() block runs calling FirstFunction() then it skips the .catch() block continuing on to the next .then() block calling SecondFunction().

The whole time the data object is being passed from Promise to Promise and being checked for its “Success”.
If at any time any of the functions have an error they return data.Success = false, and the if() block catches it and just returns the data object. This will repeat until the end of the chain is reached and the error can be caught and handled.

The downside to using Promises to chain functions in this way is it limits your ability to recover from Errors, but it is still a somewhat interesting effect of how Promises work. Probably not feasible in many situations.

Although who wouldn’t want to have to maintain code like this :stuck_out_tongue_closed_eyes: :

startPromiseChain.then(function(data) {
    return FunctionOne(data);
}).then(function(data) {
    if(data.Success) {
        return FunctionTwo(data);
    } else {
        return data;
    }
}).catch(function(data) {
    if(data.Success) {
        return FunctionTwo(data);
    } else {
        return data;
    }
}).then(function(data) {
    if(data.Success) {
        return FunctionThree(data);
    } else {
        return data;
    }
}, function(data) {
    if(data.Success) {
        return FunctionFour(data);
    } else {
        return data;
    }
}).then(function(data) {
    if(data.Success) {
        return FunctionFour(data);
    } else {
        return data;
    }
}).then(function(data) {
    if(data.Success) {
        console.log("Success!");
    } else {
        console.log("Error: " + data.Msg);
    }
});

:grinning: I think im a little crazy :stuck_out_tongue_closed_eyes:
Then again I am a programmer. :wink:

1 Like

This is really cool :smiley:

Actually I just found a way to remove one of the .then() statements and make it a little more manageable
Here is the way I was doing the startPromiseChain:

var startPromiseChain = new Promise(function(continue, skip) {
    var initialData = { One : "two", Three : "Four" }
    if(SkipFirstFunction) {
        initialData.Success = true ;
        skip(initialData);	
    } else {
        continue(initialData);
    }
});

So if the first function needed to be run I was just passing the data through to the first .then() statement and calling it in there, however I can instead do this:

var startPromiseChain = new Promise(function(continue, skip) {
    var initialData = { One : "two", Three : "Four" }
    if(SkipFirstFunction) {
        initialData.Success = true ;
        skip(initialData);	
    } else {
        continue(FirstFunction(initialData));
    }
});

This calls the first function right in the “starter” promise removing the need for one of the .then() statments.
The new chain would look like this:

startPromiseChain.then(function(data) {
    if(data.Success) {
       	return SecondFunction(data);
    } else {
	return data;
    }
}).catch(function(data) {
    if(data.Success) {
        return SecondFunction(data);
    } else {
        return data;
    }
}).then(function(data) {
    if(data.Success) {
        console.log("Success!");
    } else {
        console.log("Error: " + data.Msg);
    }
});

the .catch() still runs if Skip() is called, but by calling FirstFunction() in the initial Promise we don’t have to call it in the first .then() statment. So it makes the code at least a little easier to follow :sunglasses: