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 🙏

© 2024 – Pkg Stats / Ryan Hefner

battery-friendly-timer

v1.0.1

Published

Mobile applications using `setInterval` to poll a server are a battery hogs. Save battery life by fetching data at the right moment.

Downloads

2,113

Readme

Battery-Friendly Timer

Mobile applications using setInterval to poll a server are a battery hogs. Save battery life by fetching data at the right moment.

Motivation

Using AJAX polling on mobile is a very bad idea, because it drains the battery extremely fast. But why is that exactly?

Mobile devices use radio transmitters to communicate via 3G/4G, and their radio components are power hungry. Network providers and phone manufacturers have invented an escalation protocol to save battery life, called Radio Resource Control (RRC). Most of the time, a mobile device is idle, and uses low-power mode. When the user asks for an HTTP resource, the mobile device switches to high-power mode, downloads the resource, then switches back to low-power mode after a few seconds. The process of escalating to high power mode implies asking the radio tower for a dedicated channel and bandwidth allocation, and takes time and energy.

Therefore, mobile data network are optimized for burst: it's better to download all the data you need within a few seconds, then return to low-power mode. AJAX polling prevents the return to the low power mode, and keeps the device in high power mode until the battery is drained - even if it's only to download a few dozen bytes every minute or so.

Instead of polling a server at regular interval, you'd better call it when a download is already occurring. In that case, you know the device is in high power mode, and it's the ideal time to use the network.

The Battery-Friendly Timer listens to AJAX calls, and then triggers timeouts and intervals.

Usage

Usage is similar to setInterval, except you need to pass two delays instead of just one:

import timer from 'battery-friendly-timer';

timer.setInterval(
    () => fetch('http://my.api.url/').then(/* ... */),
    60 * 1000, // tryDelay: one minute
    60 * 60 * 1000 // forceDelay: one hour
);

setInterval takes two delays:

  • the first is the tryDelay. The timer does it best to trigger the callback at this interval, but only when the network is actively used. Therefore, if there is no network activity, the interval may never be called.
  • the second is the forceDelay. Whether there is network acticity or not, the callback will be triggered at that interval.

In the previous example, the server is polled every 60 seconds if there is an active HTTP connexion. If not, the server is polled every hour.

The Timer object provides setTimeout, clearTimeout, setInterval, and clearInterval methods. Apart from the forceDelay argument, the signature of these methods is the same as the window methods.

Example: Suggest refresh after code update of a Single-Page-Application

This scripts displays a banner inviting the user to reload the application if the JS code has changed on the server side:

<div id="update-available" style="position: absolute; top: 10px; right: 10px; padding: 1em; background-color: bisque; border-radius: 5px; display: none;">
    Myapp has a new version.
    <a href="#" onClick="window.location.reload(true);return false;">Click to reload</a>
</div>
import timer from 'battery-friendly-timer';

let previousHtml;
timer.setInterval(
    () => fetch('http://my.app.url/index.html')
        .then(response => {
            if (response.status !== 200) {
                throw new Error('offline');
            }
            return response.text();
        })
        .then(html => {
            if (!previousHtml) {
                previousHtml = html;
                return;
            }
            if (previousHtml !== html) {
                previousHtml = html;
                document.getElementById('update-available').style.display = 'block';
            }
        })
        .catch(err => { /* do nothing */ }),
    5 * 60 * 1000, // tryDelay: 5 minutes
    24 * 60 * 60 * 1000 // forceDelay: 1 day
);

This works if you use Webpack, because the index.html is small, and includes a different cache buster param for the JS script each time you deploy. For instance, here is a typical index.html generated by Webpack:

<!DOCTYPE html>
<html>
    <head>
        <title>MyApp</title>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/javascript" src="/main.js?184c0441d4c89b34ba08"></script>
    </body>
</html>

In that case, comparing the HTML source is a fast and easy way to detect changes in the JS code.

If the HTML is bigger, instead of comparing the HTML source, you can compare a hash of the source. See for instance http://stackoverflow.com/a/7616484/1333479 for a fast and simple JS hash function for strings.

FAQ

Why not use Service Workers to intercept fetch()?

Because Service Workers are restricted to HTTPS, and require a more complex setup (loading an additional script).

Why not use push notifications?

If you can use push notifications (e.g. in a hybrid app), by all means do so. It's the best compromise in terms of reactivity and battery life. But web apps don't have an easy access to push notifications, and AJAX polling is the usual fallback.

Does it work if I use XHR instead of fetch()?

No, the timer only listens to fetch() calls. If you're still using XHR in a single-page application, it's time to make the switch.