javascript - 尝试在注册服务 worker 之前运行获取功能。

标签 javascript es6-promise service-worker race-condition

我正在尝试运行一个获取函数,该函数将从我的服务器获取一些值。但由于某种原因,服务 worker 的注册首先触发,并且它留下未定义的“app_key”导致它抛出异常。 看起来这是一个我不知道如何解决的竞争条件。

我不确定应该如何将值分配给变量以确保它们对其他函数可见。任何建议,将不胜感激。

project_url = window.location.hostname;

var vapi_key;
var app_key;
var pushSub;
var end_point = "https://" + project_url + "/push/subscribe/create";

(() =>  {
    return fetch ("https://" + project_url + "/push/get_public_key"
    ).then((response) => {
        return response.json();
    })
    .then((responseJSON) => {
        vapi_key = responseJSON;
        urlBase64ToUint8Array(vapi_key);
        console.log("VAPI_KEY: ", vapi_key);
        console.log("APP_KEY: ", app_key)
    }).catch((err) => {
        console.log('Fetch Error : ', err);
    });
})();


(() => {
    return new Promise((resolve, reject) => {
        const permissionResult = Notification.requestPermission((result) => {
            resolve(result);
        });

        if (permissionResult) {
            permissionResult.then(resolve, reject);
        }
    })
        .then((permissionResult) => {
            if (permissionResult !== 'granted') {
                throw new Error('We weren\'t granted permission.');
            }
        });
})();

(() => {
    if ('serviceWorker' in navigator && 'PushManager' in window) {
        navigator.serviceWorker.register('/sw_bundle.js')
            .then((registration) => {
                let subscribeOptions = {
                    userVisibleOnly: true,
                    applicationServerKey: app_key,
                };

                return registration.pushManager.subscribe(subscribeOptions);
            })
            .then((pushSubscription) => {
                console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
                const subscriptionObject = {
                    endpoint: pushSubscription.endpoint,
                    keys: {
                        p256dh: pushSubscription.keys['p256dh'],
                        auth: pushSubscription.keys['auth']
                    }
                };
                sendSubscriptionToBackEnd(subscriptionObject);
                return pushSubscription;
            });
    } else {
        console.warn('Push messaging is not supported');
    }
})();

function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/');
    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);
    for (var i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    app_key = outputArray;
}

最佳答案

我不确定您为什么希望它起作用。特别是为什么你使用 IIFE是创造 promise 。无论如何,如果您希望将一个 promise 链接到另一个 promise ,您需要通过在 .then 调用第一个 promise 中创建第二个 promise 来明确这一点。这是一个例子:

const createPromiseA = () => new Promise((resolve) => {
window.setTimeout(() => { console.log('promiseA'); resolve(); }, 2000);
});

const createPromiseB = () => new Promise((resolve) => {
window.setTimeout(() => { console.log('promiseB'); resolve(); }, 3000);
});

createPromiseA().then(createPromiseB);

在您的情况下,您需要执行以下操作:

const fetchKeys = () => fetch ("https://" + project_url + "/push/get_public_key"
    ).then((response) => {
        return response.json();
    })
    .then((responseJSON) => {
        vapi_key = responseJSON;
        urlBase64ToUint8Array(vapi_key);
        console.log("VAPI_KEY: ", vapi_key);
        console.log("APP_KEY: ", app_key)
    }).catch((err) => {
        console.log('Fetch Error : ', err);
    });

const registerWorker = () => {
    if ('serviceWorker' in navigator && 'PushManager' in window) {
        navigator.serviceWorker.register('/sw_bundle.js')
            .then((registration) => {
                let subscribeOptions = {
                    userVisibleOnly: true,
                    applicationServerKey: app_key,
                };

                return registration.pushManager.subscribe(subscribeOptions);
            })
            .then((pushSubscription) => {
                console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
                const subscriptionObject = {
                    endpoint: pushSubscription.endpoint,
                    keys: {
                        p256dh: pushSubscription.keys['p256dh'],
                        auth: pushSubscription.keys['auth']
                    }
                };
                sendSubscriptionToBackEnd(subscriptionObject);
                return pushSubscription;
            });
    } else {
        return Promise.reject('Push messaging is not supported');
    }
};

// …

fetchKeys().then(registerWorker)
.then(() => { console.log('(!) Success!'); });

注意:我认为,使用 IIFE 的更好方法是将整个脚本包装在一个函数中并执行它。这会将变量绑定(bind)到函数的局部范围而不公开它们。 IE。做类似的事情:

(() => {
    project_url = window.location.hostname;

    var vapi_key;
    var app_key;
    var pushSub;
    var end_point = "https://" + project_url + "/push/subscribe/create";

    // ...

    const fetchKeys = () => { ... } ;
    const registerWorker = () => { ... } ;

    // ...

    fetchKeys().then(registerWorker)
    .then(() => { console.log('(!) Success!'); });

})();

关于javascript - 尝试在注册服务 worker 之前运行获取功能。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52162489/

相关文章:

javascript - 通过 JavaScript 嵌入 Instagram

javascript - React ES6 App - 本地 API 调用

typescript - 在 JS 中测试异步函数 - 错误 : "Did you forget to use await"

javascript - 在 setTimeout 中调用异步函数

javascript - 使用 setTimeout 的自定义对象调用方法丢失范围

javascript - Owl-Carousel,一次滚动两个元素

javascript - 仅在特定字符之间出现时替换文本

javascript - PWA 离线模式无法从移动浏览器的缓存中加载

vue.js - Vue js + 获取 Service Worker 注册对象时遇到问题

javascript - 如何将数据从服务器传递到 Service Worker