DEV Community

Serhii Pimenov
Serhii Pimenov

Posted on

Using the reduce method to sequentially execute a promise queue

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(){
        ...
    })
}

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

chain([{...}, {...}, ...])

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);
    }
}

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());
}

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"
    }
]);

Result

Top comments (0)