JSFiddle - React, Tailwind, and code Playground

JavaScript

Number.prototype.padLeft = String.prototype.padLeft = function(char, len) {
    var out = "",
        cLen = char.toString().length,
        mLen = this.toString().length,
        cAdd = Math.floor((len - mLen) / cLen),
        cExt = (len - mLen) % cLen,
        i = 0;

    for (; i < cAdd; i++) out += char;
    return out + char.toString().substring(0, cExt) + this;
};

function TaskTimer(name, durationInSeconds, onEnd, onTick) {
    var endTime,
        self = this, 
        running = false;
    this.name = name;
    this.totalSeconds = durationInSeconds;

    var go = (function tick() {
        var now = new Date().getTime();
        if (now >= endTime) {
            if (typeof onEnd === "function") onEnd.call(self);
            return;
        }
        self.totalSeconds = Math.round((endTime - now) / 1000);
        if (typeof onTick === "function") onTick.call(self);
        window.setTimeout(tick, 1000 / 12);
    });

    this.start = function() {
        if (running) return;
        
        running = true;
        endTime = new Date().getTime() + durationInSeconds * 1000;
        go();
    };
}
TaskTimer.prototype.toTimeString = function() {
    var hrs = Math.floor(this.totalSeconds / 60 / 60),
        min = Math.floor(this.totalSeconds / 60 - hrs * 60),
        sec = this.totalSeconds % 60;

    return [hrs.padLeft("0", 2), min.padLeft("0", 2), sec.padLeft("0", 2)].join(" : ");
};

var task = new TaskTimer("task1", 30, function() {
    document.body.innerHTML = this.toTimeString();
    alert('done');
}, function() {
    document.body.innerHTML = this.toTimeString();
});

task.start();