운영하는 홈페이지 중에 session을 최초 발생후 1시간마다 갱신하면서 유지하는 기능을 갖고 있는데 safari브라우저에서 랜덤하게 세션이 유실되는 현상이 발생되었다.
Flow
- 동작은 아래 순서와 같다
1. 접속(세션발생)
2. 활동페이지(세션유지, 1시간 limit, 연장 버튼을 통해 연장가능)
3. 저장(세션확인)
활동페이지의 타이머는 자바스크립트의 setInterval 함수를 이용하여 구현하였고 새 탭 전환 혹은 비활성화시 동작을 하지 않는 것을 간단한 테스트를 통하여 확인 할 수 있었다.
TEST1
- setInterval 테스트
- safari브라우저에서 해당페이지를 켜둔 상태로 다른탭 전환 혹은 비활성화시 count가 증가하는지
<div id='time'>0</div>
<script>
//1초마다 setInterval을 통해 1씩 증가
window.onload=function(){
setInterval(function(){
document.getElementById('time').innerHTML = ++(parseInt(document.getElementById('time').innerHTML,10))
},1000);
}
</script>
문득 이런 의문이 들었다.. 혹시.. setinterval이 문제냐..??
TEST2
- setInterval 함수가 문제일 수 있다? settimeout으로 변경
- 0부터 100까지 1초마다 증가하는 하도록 소스 작성 후 모바일 사파리에서 테스트
TIMER TEST
<div id="time">0</div>
<script>
//settimeout을 통해 1초씩 증가
window.onload = function(){
var targetEl = function(){
return document.querySelectorAll("#time")[0];
},
getNum = function(){
return parseInt(targetEl().innerHTML,10);
},
setNum = function(num){
targetEl().innerHTML = num;
}
,timeout = function(maxSec){
return function timeout(){
if (!maxSec) return clearTimeout(t);
var t = setTimeout(function(){
var currentNum = getNum();
setNum(currentNum+=1);
maxSec-=1;
timeout();
clearTimeout(t);
},1000);
}();
};
timeout(100);
};
</script>
worker로 하면 동작하지 않을까??
TEST3
- 0부터 100까지 1초마다 증가하는 하도록 소스 작성 후 모바일 사파리에서 테스트
//main.html
TIMER TEST
<div id="time">0</div>
<script>
window.onload = function(){
var targetEl = function(){
return document.querySelectorAll("#time")[0];
},
getNum = function(){
return parseInt(targetEl().innerHTML,10);
},
setNum = function(num){
targetEl().innerHTML = num;
}
var myWorker = new Worker('worker.js');
myWorker.onmessage = function(num){
setNum(parseInt(num,10));
});
};
</script>//worker.js
var i = 0;
self.onmessage = function( e ) {
setTimeout( function() {
postMessage( ++i );
}, 1000 );
};
역시나 동작하지 않는다..
문제분석
- safari 브라우저에서 setInterval, requestAnimationFrame함수 등 여러 자바스크립트가 비활성화 혹은 저성능으로 동작이 된다.
브라우저별 동작
- Chrome : Chrome 탭이 비활성화된 경우 setInterval의 최소 간격을 약 1000ms로 제한합니다. 간격이 1000ms보다 크면 지정된 간격으로 실행됩니다. 창 초점과 관계없이 일정하게 동작
- Firefox : Firefox는 Chrome과 마찬가지로 탭(창 제외)이 비활성화된 경우 setInterval의 최소 간격을 약 1000ms로 제한합니다. 그러나 requestAnimationFrame은 탭이 비활성화되면 기하급수적으로 느리게 실행되며 각 프레임은 1초, 2초, 4초, 8초 등이 소요됩니다.
- Internet Explorer : IE는 탭이 비활성일 때 setInterval의 지연을 제한하지 않지만 비활성 탭에서 requestAnimationFrame을 일시 중지합니다. 창의 초점이 맞지 않는지는 중요하지 않습니다.
- Edge : Edge 14부터 setInterval은 비활성 탭에서 1000ms로 clamping 됩니다. requestAnimationFrame은 비활성 탭에서 항상 일시 중지됩니다.
- Safari : Safari 탭전환 혹은 비활성화 시 일시중지 됨. requestAnimationFrame도 일시 중지됩니다.
- Opera : 웹킷 엔진이 도입된 이후 오페라는 크롬과 같은 동작을 보입니다. setInterval은 1000ms에 clamping이 적용되며, 탭이 비활성화되면 requestAnimationFrame이 일시 중지됩니다.
기타
- 비활성 탭에서 타임아웃이1000ms에 여러 번 일어날 경우
- 부하와 배터리 사용양을 줄이기 위해서, 비활성화 탭들에서 타임아웃이 1초에 여러번 일어나지 않도록 “clamping” 됩니다.
Firefox는 5버전부터 이 동작을 구현했습니다. (bug 633421참고, 1000ms 상수는dom.min_background_timeout_value
설정을 통해 수정할 수 있습니다)
Chrome은 11버전부터 구현했습니다 (crbug.com/66078).
Android용 Firefox는 bug 736602 이후 버전 14부터 백그라운드 탭에 15분의 타임아웃을 사용하고, 완전히 unload도 할 수 있습니다.