電商網站,團購類網站很多都有都有秒殺這個業務。秒殺自然涉及前端倒計時的問題。前端倒計時許考慮兩個問題:
1.由於js是單線程的,也就是阻塞的,定時可定會不准。無論setTimeout()還是setInterval(),都有問題;
2、打開瀏覽器,然后切換到其他app,再次回到瀏覽器,這期間js可能停止執行的問題。
本質上來說,只需要兩個時間點就可以了:當前時間、秒殺開始時間。有了這兩個數據,我們就可以倒計時了。兩個時間的差值就是我們要倒計時的時間差,每隔1秒減少1/每隔1毫秒減少1。但是,當前的時間不要用new Date(),這是獲取手機的時間,如果用戶修改手機的時間,這個會跟隨變化的。基於此,當前的時間必須是服務端傳過來的。用戶每次打開網頁,都會傳服務器的當前時間。秒殺開始的時間一般是后台配置好的,只要配置了,他就定了。是個不變的量。
解決方法1:
以服務器的當前時間為倒計時的當前時間,上面提到需要考慮的那兩個問題都沒有解決,如果我們實時同步服務器的當前時間,那么這兩個問題就都解決了。
如何時時同步呢?最先想到的是,時時調用一個后端的接口,這個接口返回服務器的當前時間。但實際上,我們只需要跟服務器ajax交互就可以了,請求一個服務器不存在的資源,我們從返回中拿返回頭,再從這個頭中取服務器的當前時間。
這種方法就是不斷的同步服務器的事件。
詳細代碼參考:https://www.zhihu.com/question/28896402
https://zhuanlan.zhihu.com/p/20832837?refer=xiaojue
筆者的代碼:
var this_start_time//指定秒殺開始的時間點,服務器傳過來的 var this_count_down;//時間差:指定秒殺開始時間減去當前時間。可以先不初始化。 //校准倒計時的秒數函數 function sync(){ var xhr = new XMLHttpRequest(); xhr.open('get', '/', true); xhr.onreadystatechange = function() { if (xhr.readyState === 3) { var now = xhr.getResponseHeader('Date'); var nowTime = new Date(now).valueOf()/1000;//沒有毫秒值,我們倒計時的最小單位是秒。有毫秒,不除以1000 this_count_down =this_start_time - nowTime; xhr.abort(); } }; xhr.send(null); } //先對this_count_down初始化 sync(); if(this_count_down>0){ setInterval(function(){ sync(); this_count_down--; },1000) }else{ alert('倒計時結束 ') }
以上代碼的欠缺是每次請求服務器,性能不佳。但好處也顯而易見,3個用戶同時訪問頁面,他們的倒計時結果基本上一樣的,秒殺變得比較公平。當服務器出現問題,請求不到服務器的當前時間,這個代碼會有問題,但這不是我們考慮的問題。
解決方法2:
還有種解決的辦法,就是計算每次倒計時的誤差,你能知道第n次你倒計時的應該發生的時間,再跟當前的時間對比,這個誤差就在下次倒計時的時候考慮進去。這樣可以保證誤差不累計。但是對於退出瀏覽器的行為,這種其實是有問題的。
這種方法是不斷校准倒計時的周期,而周期不可能為負數,治標不治本。
詳細代碼參考:http://www.cnblogs.com/flash3d/archive/2014/05/08/3715600.html
2種方法,第1種更好,不會有任何問題。而第2種,筆者實驗的過程中會有點問題。