npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@freakynit/background-task-runner

v1.0.1

Published

A simple but robust Node.js library for running background tasks with configurable retry logic, error handling, and graceful shutdown capabilities.

Readme

BackgroundTaskRunner

A simple but robust Node.js library for running background tasks with configurable retry logic, error handling, and graceful shutdown capabilities.

Features

  • Periodic task execution with configurable intervals
  • Retry mechanisms with exponential, linear, or constant backoff strategies
  • Custom error handling with error callbacks
  • Flexible logging support for console, Winston, Pino, and other loggers
  • Graceful shutdown with proper cleanup
  • Task timeouts to prevent hanging operations
  • Thread-safe operations with countdown latch pattern

Installation

See note below

npm install @freakynit/background-task-runner

Basic Usage

const { BackgroundTaskRunner } = require('@freakynit/background-task-runner');

// Simple task that runs every 30 seconds
const task = async (config) => {
    console.log('Executing background task...');
    // Your task logic here
    await someAsyncOperation();
};

const runner = new BackgroundTaskRunner(task, {
    pollingPeriodSeconds: 30
});

// Start the background runner
await runner.start();

// Later, stop gracefully
await runner.stopAndWait();

Configuration Options

const config = {
    pollingPeriodSeconds: 60,        // Run every 60 seconds
    maxRetries: 5,                   // Retry up to 5 times on failure
    baseRetryDelayMs: 1000,          // Base delay of 1 second for retries
    backoffStrategy: "exponential",  // "exponential", "linear", or "constant"
    initialDelayMs: 5000,            // Wait 5 seconds before first run
    taskTimeoutMs: 30000,            // Timeout tasks after 30 seconds
    logTag: "my-background-task",    // Custom log prefix
    onError: (error, context) => {  // Custom error handler
        // Handle errors here
    },
    logger: customLogger             // Custom logger instance
};

Error Handling Example

const { BackgroundTaskRunner } = require('@freakynit/background-task-runner');

const unreliableTask = async () => {
    // Simulate a task that fails occasionally
    if (Math.random() < 0.3) {
        throw new Error('Random task failure');
    }
    console.log('Task completed successfully');
};

const runner = new BackgroundTaskRunner(unreliableTask, {
    pollingPeriodSeconds: 10,
    maxRetries: 3,
    backoffStrategy: "exponential",
    onError: (error, context) => {
        console.error(`Task failed after ${context.attempts} attempts:`);
        console.error(`Original error: ${context.originalError.message}`);
        
        // Send to monitoring service
        // sendToMonitoring(error, context);
        
        // Send alert if needed
        if (context.attempts >= context.maxRetries) {
            // sendAlert(`Background task ${context.logTag} failed permanently`);
        }
    }
});

await runner.start();

Custom Logger Example

const { BackgroundTaskRunner } = require('@freakynit/background-task-runner');
const winston = require('winston');

// Create a Winston logger
const customLogger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({ filename: 'background-tasks.log' }),
        new winston.transports.Console()
    ]
});

const task = async () => {
    // Your task logic
    await processData();
};

const runner = new BackgroundTaskRunner(task, {
    pollingPeriodSeconds: 120,
    logger: customLogger,
    logTag: "data-processor"
});

await runner.start();

Backoff Strategies

Exponential Backoff

const runner = new BackgroundTaskRunner(task, {
    backoffStrategy: "exponential",
    baseRetryDelayMs: 1000,
    maxRetries: 4
});
// Retry delays: 1s, 2s, 4s, 8s

Linear Backoff

const runner = new BackgroundTaskRunner(task, {
    backoffStrategy: "linear",
    baseRetryDelayMs: 2000,
    maxRetries: 3
});
// Retry delays: 2s, 4s, 6s

Constant Backoff

const runner = new BackgroundTaskRunner(task, {
    backoffStrategy: "constant",
    baseRetryDelayMs: 5000,
    maxRetries: 5
});
// Retry delays: 5s, 5s, 5s, 5s, 5s

Task Timeout Example

const longRunningTask = async () => {
    // Simulate a task that might hang
    await new Promise(resolve => setTimeout(resolve, 45000)); // 45 seconds
};

const runner = new BackgroundTaskRunner(longRunningTask, {
    taskTimeoutMs: 30000, // Timeout after 30 seconds
    onError: (error, context) => {
        if (error.message.includes('timed out')) {
            console.log('Task was terminated due to timeout');
        }
    }
});

Graceful Shutdown

const runner = new BackgroundTaskRunner(task, {
    pollingPeriodSeconds: 60
});

await runner.start();

// Handle shutdown signals
process.on('SIGTERM', async () => {
    console.log('Received SIGTERM, shutting down gracefully...');
    await runner.stopAndWait();
    process.exit(0);
});

process.on('SIGINT', async () => {
    console.log('Received SIGINT, shutting down gracefully...');
    await runner.stopAndWait();
    process.exit(0);
});

API Reference

Constructor

new BackgroundTaskRunner(taskFn, config)
  • taskFn (Function): The async function to execute periodically
  • config (Object): Configuration options (optional)

Methods

start()

Starts the background task runner.

await runner.start();

stop()

Stops the background task runner immediately.

runner.stop();

awaitShutdown()

Waits for the current task to complete before resolving.

await runner.awaitShutdown();

stopAndWait()

Combines stop() and awaitShutdown() for graceful shutdown.

await runner.stopAndWait();

Real-world Example

const { BackgroundTaskRunner } = require('@freakynit/background-task-runner');
const { DatabaseConnection } = require('./database');
const { EmailService } = require('./email-service');

// Task to process pending email queue
const emailProcessorTask = async (config) => {
    const db = new DatabaseConnection();
    const emailService = new EmailService();
    
    try {
        const pendingEmails = await db.getPendingEmails();
        
        for (const email of pendingEmails) {
            await emailService.send(email);
            await db.markEmailAsSent(email.id);
        }
        
        console.log(`Processed ${pendingEmails.length} emails`);
    } finally {
        await db.close();
    }
};

const emailRunner = new BackgroundTaskRunner(emailProcessorTask, {
    pollingPeriodSeconds: 30,        // Check every 30 seconds
    maxRetries: 3,                   // Retry failed batches 3 times
    backoffStrategy: "exponential",  // Exponential backoff for retries
    taskTimeoutMs: 120000,           // 2 minute timeout
    logTag: "email-processor",
    onError: (error, context) => {
        console.error(`Email processing failed: ${error.message}`);
        // Could send alert to ops team here
    }
});

await emailRunner.start();
console.log('Email processor started');

License

This project is released under an open-source license. See the LICENSE file for details.