为什么 serviceworker 导致每秒 jquery post 立即停止?
Why does serviceworker cause every second jquery post to instantly stall out?
我在标题中所说的只发生在 chrome 中,因为在 firefox 中“('serviceWorker' 在导航器中)”总是错误的,而是 "console.warn('Service workers aren\'t supported in this browser.');" 触发器。
如果您清除浏览器或 运行 隐身模式,那么它最初可以正常工作,但是当您第一次注销然后重新登录时,问题就开始了。
This is what it looks like in the network log
这是我注册软件的代码:
function activateSW(){
if('serviceWorker' in navigator){
if(window.location.pathname != '/'){
//register with API
if(!navigator.serviceWorker.controller) navigator.serviceWorker.register('/service-worker', { scope: '/' });
//once registration is complete
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
//get subscription
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){
//enable the user to alter the subscription
$('.js-enable-sub-test').removeAttr("disabled");
//set it to allready subscribed if it is so
if(subscription){
$('.js-enable-sub-test').prop("checked", true);
$('.js-enable-sub-test').parent().addClass('checked');
}
});
});
}
}else{
console.warn('Service workers aren\'t supported in this browser.');
}
}
'/service-worker' 是发送到 index.php 的请求(通过 .htaccess)。它最终在这个函数中结束:
function serviceworkerJS($params){
$hash = API::request('settings', 'getSWHash', '');
if($hash != false){
setcookie('SW_Hash', $hash, time() + (86400 * 365 * 10), "/");
header('Content-type: text/javascript');
echo "'use strict';
var hash = '".$hash."';";
include(ROOT_PATH.'public/js/service-worker.js');
}elseif(isset($_COOKIE['SW_Hash'])){
header('Content-type: text/javascript');
echo "'use strict';
var hash = '".$_COOKIE['SW_Hash']."';";
include(ROOT_PATH.'public/js/service-worker.js');
}else{
header('HTTP/1.1 404 Not Found');
}
}
Service-worker.js 如 chrome://serviceworker-internals/ 中所示,如下所示:
(对地址的多次引用已用星号代替)
'use strict';
var hash = 'bd8e78963deebf350f851fbf8cdc5080';
var *****_API_ENDPOINT = 'https://*********.***/';
//For displaying notifications
function showNotification(title, body, icon, data, id) {
var notificationOptions = {
body: body,
icon: icon,
tag: id,
data: data
};
//possibly unnecessary
if(self.registration.showNotification){
return self.registration.showNotification(title, notificationOptions);
}else{
return new Notification(title, notificationOptions);
}
}
//asks the server for messages and sends them for displaying.
function getMessages(event){
//showNotification('debug', 'initial', '', '', 'debug1');
//build question
var FD = new FormData();
FD.append('hash', hash);
//ask start20 for the notifications
event.waitUntil(
fetch(*****_API_ENDPOINT + 'ajax-get-SW-notification/', {method: 'post', body: FD}).then(function(response){
//something went wrong
if (response.status !== 200){
console.log('Error communicating with ******, code: ' + response.status);
showNotification('debug', 'picnic', '', '', 'debug2');
throw new Error();
}
//decode the response
return response.json().then(function(data){
var len = data.notifications.length;
//showNotification('debug', len, '', '', 'propertyName');
//Display
for(var i = 0; i < len -1; i++){
showNotification(data.notifications[i].title,
data.notifications[i].body,
data.notifications[i].imageurl,
data.notifications[i].linkurl,
data.notifications[i].hash);
}
//the last one needs to be returned to complete the promise
return showNotification(data.notifications[len -1].title,
data.notifications[len -1].body,
data.notifications[len -1].imageurl,
data.notifications[len -1].linkurl,
data.notifications[len -1].hash);
});
})
);
}
//when the user installs a new SW
/*self.addEventListener('activate', function(event){
//getMessages(event);
//event.preventDefault();
event.waitUntil(return self.registration.showNotification('bicnic', { body: '*p' }));
});*/
//when the serviceworker gets a puch from the server
self.addEventListener('push', function(event){
getMessages(event);
event.preventDefault();
});
//get the link associated witht he message when a user clicks on it
self.addEventListener('notificationclick', function(event){
//ask if the notification has any link associated with it
var FD = new FormData();
FD.append('hash', event.notification.tag);
//get the link
event.waitUntil(
fetch(******_API_ENDPOINT + 'ajax-notification-has-link/', {method: 'post', body: FD}).then(function(response){
//something went wrong
if (response.status !== 200){
console.log('Error communicating with ********, code: ' + response.status);
return;
}
//decode the response
return response.json().then(function(data){
//if there's a link associated with the message hash
if(data.link){
console.log(******_API_ENDPOINT + 'notification-link/' + event.notification.tag);
return clients.openWindow(*****_API_ENDPOINT + 'notification-link/' + event.notification.tag);
}
});
})
);
});
//unnecessary?
/*self.addEventListener('install', function(event){
//event.preventDefault();
});
self.addEventListener("fetch", function(event) {
});//*/
现在,如果您发表评论 "if(!navigator.serviceWorker.controller) navigator.serviceWorker.register( '/service-worker', { scope: '/' });",那么问题就会消失,但是 serviceworker 订阅和取消订阅会停止工作。 (if语句好像作用不大,只是为了解决这个问题而添加的)
我已经尝试了多种版本的 activateSW() ,针对 运行 的不同表达式使用了不同的条件,但还没有设法制作出一个可以在不破坏 serviceworker 的情况下运行的版本。我也曾尝试在各个方面(注册、帖子)捕获错误,但这没有成功,因为 none 其中有任何错误。
我怀疑问题可能在于,当您注册 serviceworker 时,会触发激活事件。现在,如果你抓住这个并完成事件承诺,那么你将无法订阅。但是我怀疑 serviceworker 在您注销时仍然处于活动状态,这会导致问题。
如果您有任何疑问或想查看更多代码,请直接提问。
编辑;这是解决方案:
self.addEventListener("fetch", function(event) {
event.respondWith(
fetch(event.request)
);
});
您在 Chrome DevTools 的网络面板中看到的是令人困惑的噪音,但应该不会对您的应用程序的行为产生实际影响。
如果服务工作者控制页面但不包含 fetch
事件处理程序,则 Chrome(M44 稳定版和 M46 金丝雀版)的当前版本最终会将所有网络请求记录在具有 (canceled)
状态 like you're seeing 的网络面板。实际请求仍然在没有 service worker 干预的情况下发出,这是第二个成功的记录条目。从您页面的角度来看,事情应该按预期工作,但我完全理解这种困惑。
我听说有计划使 Chrome 的服务工作者实现在发出网络请求并且没有 fetch
事件处理程序时表现得更像 "no-op" .我不知道这些计划进行了多长时间,但我希望一旦发生这种情况,您所看到的噪音就会消失。
我在标题中所说的只发生在 chrome 中,因为在 firefox 中“('serviceWorker' 在导航器中)”总是错误的,而是 "console.warn('Service workers aren\'t supported in this browser.');" 触发器。
如果您清除浏览器或 运行 隐身模式,那么它最初可以正常工作,但是当您第一次注销然后重新登录时,问题就开始了。
This is what it looks like in the network log
这是我注册软件的代码:
function activateSW(){
if('serviceWorker' in navigator){
if(window.location.pathname != '/'){
//register with API
if(!navigator.serviceWorker.controller) navigator.serviceWorker.register('/service-worker', { scope: '/' });
//once registration is complete
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration){
//get subscription
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){
//enable the user to alter the subscription
$('.js-enable-sub-test').removeAttr("disabled");
//set it to allready subscribed if it is so
if(subscription){
$('.js-enable-sub-test').prop("checked", true);
$('.js-enable-sub-test').parent().addClass('checked');
}
});
});
}
}else{
console.warn('Service workers aren\'t supported in this browser.');
}
}
'/service-worker' 是发送到 index.php 的请求(通过 .htaccess)。它最终在这个函数中结束:
function serviceworkerJS($params){
$hash = API::request('settings', 'getSWHash', '');
if($hash != false){
setcookie('SW_Hash', $hash, time() + (86400 * 365 * 10), "/");
header('Content-type: text/javascript');
echo "'use strict';
var hash = '".$hash."';";
include(ROOT_PATH.'public/js/service-worker.js');
}elseif(isset($_COOKIE['SW_Hash'])){
header('Content-type: text/javascript');
echo "'use strict';
var hash = '".$_COOKIE['SW_Hash']."';";
include(ROOT_PATH.'public/js/service-worker.js');
}else{
header('HTTP/1.1 404 Not Found');
}
}
Service-worker.js 如 chrome://serviceworker-internals/ 中所示,如下所示: (对地址的多次引用已用星号代替)
'use strict';
var hash = 'bd8e78963deebf350f851fbf8cdc5080';
var *****_API_ENDPOINT = 'https://*********.***/';
//For displaying notifications
function showNotification(title, body, icon, data, id) {
var notificationOptions = {
body: body,
icon: icon,
tag: id,
data: data
};
//possibly unnecessary
if(self.registration.showNotification){
return self.registration.showNotification(title, notificationOptions);
}else{
return new Notification(title, notificationOptions);
}
}
//asks the server for messages and sends them for displaying.
function getMessages(event){
//showNotification('debug', 'initial', '', '', 'debug1');
//build question
var FD = new FormData();
FD.append('hash', hash);
//ask start20 for the notifications
event.waitUntil(
fetch(*****_API_ENDPOINT + 'ajax-get-SW-notification/', {method: 'post', body: FD}).then(function(response){
//something went wrong
if (response.status !== 200){
console.log('Error communicating with ******, code: ' + response.status);
showNotification('debug', 'picnic', '', '', 'debug2');
throw new Error();
}
//decode the response
return response.json().then(function(data){
var len = data.notifications.length;
//showNotification('debug', len, '', '', 'propertyName');
//Display
for(var i = 0; i < len -1; i++){
showNotification(data.notifications[i].title,
data.notifications[i].body,
data.notifications[i].imageurl,
data.notifications[i].linkurl,
data.notifications[i].hash);
}
//the last one needs to be returned to complete the promise
return showNotification(data.notifications[len -1].title,
data.notifications[len -1].body,
data.notifications[len -1].imageurl,
data.notifications[len -1].linkurl,
data.notifications[len -1].hash);
});
})
);
}
//when the user installs a new SW
/*self.addEventListener('activate', function(event){
//getMessages(event);
//event.preventDefault();
event.waitUntil(return self.registration.showNotification('bicnic', { body: '*p' }));
});*/
//when the serviceworker gets a puch from the server
self.addEventListener('push', function(event){
getMessages(event);
event.preventDefault();
});
//get the link associated witht he message when a user clicks on it
self.addEventListener('notificationclick', function(event){
//ask if the notification has any link associated with it
var FD = new FormData();
FD.append('hash', event.notification.tag);
//get the link
event.waitUntil(
fetch(******_API_ENDPOINT + 'ajax-notification-has-link/', {method: 'post', body: FD}).then(function(response){
//something went wrong
if (response.status !== 200){
console.log('Error communicating with ********, code: ' + response.status);
return;
}
//decode the response
return response.json().then(function(data){
//if there's a link associated with the message hash
if(data.link){
console.log(******_API_ENDPOINT + 'notification-link/' + event.notification.tag);
return clients.openWindow(*****_API_ENDPOINT + 'notification-link/' + event.notification.tag);
}
});
})
);
});
//unnecessary?
/*self.addEventListener('install', function(event){
//event.preventDefault();
});
self.addEventListener("fetch", function(event) {
});//*/
现在,如果您发表评论 "if(!navigator.serviceWorker.controller) navigator.serviceWorker.register( '/service-worker', { scope: '/' });",那么问题就会消失,但是 serviceworker 订阅和取消订阅会停止工作。 (if语句好像作用不大,只是为了解决这个问题而添加的)
我已经尝试了多种版本的 activateSW() ,针对 运行 的不同表达式使用了不同的条件,但还没有设法制作出一个可以在不破坏 serviceworker 的情况下运行的版本。我也曾尝试在各个方面(注册、帖子)捕获错误,但这没有成功,因为 none 其中有任何错误。
我怀疑问题可能在于,当您注册 serviceworker 时,会触发激活事件。现在,如果你抓住这个并完成事件承诺,那么你将无法订阅。但是我怀疑 serviceworker 在您注销时仍然处于活动状态,这会导致问题。
如果您有任何疑问或想查看更多代码,请直接提问。
编辑;这是解决方案:
self.addEventListener("fetch", function(event) {
event.respondWith(
fetch(event.request)
);
});
您在 Chrome DevTools 的网络面板中看到的是令人困惑的噪音,但应该不会对您的应用程序的行为产生实际影响。
如果服务工作者控制页面但不包含 fetch
事件处理程序,则 Chrome(M44 稳定版和 M46 金丝雀版)的当前版本最终会将所有网络请求记录在具有 (canceled)
状态 like you're seeing 的网络面板。实际请求仍然在没有 service worker 干预的情况下发出,这是第二个成功的记录条目。从您页面的角度来看,事情应该按预期工作,但我完全理解这种困惑。
我听说有计划使 Chrome 的服务工作者实现在发出网络请求并且没有 fetch
事件处理程序时表现得更像 "no-op" .我不知道这些计划进行了多长时间,但我希望一旦发生这种情况,您所看到的噪音就会消失。