Search code examples
javascriptgoogle-chromechromium

Notification.requestPermission promise is never resolved if user has quieter messaging enabled


We have a slider button on our site where users can opt in to browser push notifications. We recently noticed that the chromium feature "quieter messaging" causes some issues in our implementation. We always evaluated the returned promise of requestPermission to tell the user if their action actually worked. However, if the setting is enabled, the promise is never resolved(or rejected) unless the user clicks on "allow".

async function init() {
    var permission = await Notification.requestPermission();
    alert(permission); // This is never called in this case
}
init();

We want to tell the user if their action (enabling the notifications) actually worked, or if they need to check their browser settings. Is there a better way than having a separate timeout promise?

This is my current best guess for a workaround:

// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const promiseTimeout = function (ms, promise) {
    // Create a promise that rejects in <ms> milliseconds
    let timeout = new Promise((resolve, reject) => {
        let id = setTimeout(() => {
            clearTimeout(id);
            reject('Timed out in ' + ms + 'ms.')
        }, ms)
    })
    // Returns a race between our timeout and the passed in promise
    return Promise.race([
        promise,
        timeout
    ])
}

async function init() {
    var permissionPromise = Notification.requestPermission();
    try {
        var p = await promiseTimeout(1000/*some magic number*/, permissionPromise);
        alert(p);
    } catch (error) {
        alert(error);
    }
}
init();

Notifications blocked

Quieter messaging enabled


Solution

  • Posting my workaround as an answer since there seems to be no better other solution at this point.

    // https://italonascimento.github.io/applying-a-timeout-to-your-promises/
    const promiseTimeout = function (ms, promise) {
        // Create a promise that rejects in <ms> milliseconds
        let timeout = new Promise((resolve, reject) => {
            let id = setTimeout(() => {
                clearTimeout(id);
                reject('Timed out in ' + ms + 'ms.')
            }, ms)
        })
        // Returns a race between our timeout and the passed in promise
        return Promise.race([
            promise,
            timeout
        ])
    }
    
    async function init() {
        var permissionPromise = Notification.requestPermission();
        try {
            var p = await promiseTimeout(1000/*some magic number*/, permissionPromise);
            alert(p);
        } catch (error) {
            alert(error);
        }
    }
    init();