Using the reduce method to sequentially execute a promise queue

Serhii Pimenov - Apr 18 '20 - - Dev Community

Hello friends!

In this post, I decided to share with you one of the uses of the Array.prototype.reduce a method, namely, using the method to consistently fulfill promises.

In one of the works (AnimationJS, m4q), I needed to perform sequentially some (total number is unknown) number of animations. Each animation is a Promise.

Animation definition

function animate(args){
    return new Promise(function(){
        ...
    })
}
Enter fullscreen mode Exit fullscreen mode

Chain definition. Each argument for chain is a plain object, who describe animation.

chain([{...}, {...}, ...])
Enter fullscreen mode Exit fullscreen mode

async/await

The first thing that came to mind was to solve the problem using async/await:

async function chain(arr) {
    for(let i = 0; i < arr.length; i ++) {
        const a = arr[i];
        a.loop = false;
        await animate(a);
    }
}
Enter fullscreen mode Exit fullscreen mode

This works fine, but was not suitable for my m4q library written for Metro 4 on ECMAScript 5.1 due to lack of async/await support.

for Promise, I use polyfill

Finding a solution made it possible to use the reduce method.

function chain(arr, loop = false){
    const reducer = function(acc, item){
        return acc.then(function(){
            return animate(item)
        });
    }

    arr.reduce(reducer, Promise.resolve());
}
Enter fullscreen mode Exit fullscreen mode

How to reduce works? The reduce() method reduces the array to a single value. The reduce() method executes a provided function for each value of the array (from left-to-right). The return value of the function is stored in an accumulator (result/total). For initial value, I use empty resolve. In next, on all iteration reducer execute Peromise and call a next animation in resolver. Since the resolver is executed after the end of the promise, we get sequential animations when each next animation is executed only when the previous one is completed.

Now I can execute chain of animations:

var ball = document.querySelector(".ball");
chain([
    {
        el: ball,
        draw: {
            top: [h - ball.clientHeight]
        },
        dur: 2000,
        ease: "easeOutBounce"
        },
    {
        el: ball,
        draw: {
            left: [w - ball.clientWidth]
        },
        dur: 2000,
        ease: "easeOutBounce"
    },
    {
        el: ball,
        draw: {
            top: 0
        },
        dur: 2000,
        ease: "easeOutBounce"
    },
    {
        el: ball,
        draw: {
            left: 0
        },
        dur: 2000,
        ease: "easeOutBounce"
    }
]);
Enter fullscreen mode Exit fullscreen mode

Result

. . . . . . . . . . . .
Terabox Video Player