Coder Social home page Coder Social logo

marmelab / battery-friendly-timer Goto Github PK

View Code? Open in Web Editor NEW
25.0 16.0 2.0 11 KB

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

License: MIT License

Makefile 3.78% JavaScript 96.22%
mobile battery-life ajax-polling

battery-friendly-timer's Introduction

Battery-Friendly Timer

publication Archived Repository
The code of this repository was written to illustrate the blog post Update a Single Page App on Code Change Without Draining The Battery
This code is not intended to be used in production, and is not maintained.

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.

battery-friendly-timer's People

Contributors

alexisjanvier avatar fzaninotto avatar oluckyman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.