Christophe Chenel
Christophe Chenel

Reputation: 1921

Sequential Firebase cloud function promises

I still don't get how I could make cloud functions works sequentially. Here is my code :

export const Run = functions.database.ref("PATH").onUpdate((snap, 
context) =>
{
const Collection = [];
ref.child('PATH2').once('value', function(lambda) 
{
    lambda.forEach((snap) => 
    {
        if (snap.val().state) 
        {
            Collection.push(snap.key);
        }
        return false; //since I use typescript I must return a boolean with foreach loop
    });
}).then(() => 
{
for (let i = 0; i < Collection.length; i++) 
{
    const arg = Collection[i];

        if(Verify(arg))
        {
            break;
        }

        Function_A(arg)
        .then(() => {
            return Function_B(arg)
        })
        .then(() => {
            return Function_C(arg);
        })
        .then(() => {
            return Function_D(arg);
        })
        .then(() => {
            return Function_E(arg)
        })
        .then(() => {
            return Function_F(arg)
        })
        .then(() => {
            return Function_G(arg)
        })
}
})
});

The problem is that Function C starts before Function B has been completed. How could I make it works sequentially? I really need function B to be completely fulfilled before going to the next function.

Upvotes: 1

Views: 499

Answers (1)

Tomalak
Tomalak

Reputation: 338406

The canonical way to let a number of promises ("promise-returning asynchronous functions") run in sequence is to chain them.

Promise.resolve(init)
    .then(result => function1(result))    // result will be init
    .then(result => function2(result))    // result will be the result of function1
    .then(result => function3(result));   // result will be the result of function2
                                          // overall result will be that of function 3

// more succinctly, if each function takes the previous result
Promise.resolve(init).then(function1).then(function2).then(function3);

This pattern can be expressed generically, i.e. with a variable number of functions, using an array and a .reduce() call:

var funcs = [function1, function2, function3, functionN];
var chain = funcs.reduce((result, nextFunc) => nextFunc(result), Promise.resolve(init));

Here chain is a single promise (the last one in the chain). It will resolve when the chain has resolved.

Now, assuming that we have functions A through G, and assuming that lambda is an array of values:

const funcSequence = [Function_A, Function_B, Function_C, Function_D, Function_E, Function_F, Function_G];

const chains = lambda
    .filter(snap => snap.val().state && Verify(snap.key))
    .map(snap => funcSequence.reduce((result, func) => func(snap.key), Promise.resolve(/* init */)));

chains will be an array of promise chains (an array of last promises of each chain, precisely). All chains wills run in parallel, but each individual chain will run in sequence. All we need to do now is to wait for all of them to resolve.

Promise.all(chains).then(results => console.log(results));

Todo: Add error handling.

The above can also be done with loops and async/await. You can convert the code and see which approach you like better.

Upvotes: 1

Related Questions