原子操做這是Java多線程編程的老生常談了。所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。編程
固然JS是單線程的,因此不存在線程打斷這麼一說,我只是從Java中借引了這麼一個概念。若是一段JS代碼在執行過程當中沒有未知操做被引入,那麼這段代碼就是100%可控和安全的,這就是原子操做。反之非原子操做可能會由於外界操做的引入致使代碼變得難以控制而產生隱晦的bug。安全
下面舉例說明非原子操做可能會帶來的問題多線程
function start() { player = new Player(); player.start(); fireEvent('start'); player.resume(); fireEvent('play'); } function stop() { player.pause(); fireEvent('pause'); player.stop(); player = null; fireEvent('stop'); }
這段代碼中定義兩個方法,start表示開始播放視頻,裏面分別有兩段原子操做,在每一個原子操做結束以後都向外發送了事件;stop方法相似。代碼看起來簡單而完美,但因爲這兩個方法都不是原子操做,因此可能會存在隱患。spa
下面咱們用一樣簡單的方式使用這兩個方法就會產生混亂的結果。線程
on('start', function(){ stop(); }); start();
這段代碼試圖讓播放器一開始播放就中止,意圖明確。可是它卻會讓實際執行結果變成下面這樣code
player = new Player(); player.start(); fireEvent('start'); //監聽start事件後引入的操做 player.pause(); fireEvent('pause'); player.stop(); player = null; fireEvent('stop'); //end player.resume(); fireEvent('play');
這段代碼對外界來講竟然在stop事件發生以後還會發生一次play事件,堪稱詭異。視頻
究其緣由是由於觸發play事件後引入外部操做致使下一個原子操做所依賴的前提改變。這就是我說的非原子操做的隱患。blog
那麼如何避免這種問題呢,把代碼改爲這樣就行事件
function start() { if (!started) { player = new Player(); player.start(); started = true; fireEvent('start'); } if (started && !played) { player.resume(); played = true; fireEvent('play'); } } function stop() { if (started && played) { player.pause(); played = false; fireEvent('pause'); } if (started) { player.stop(); player = null; started = false; fireEvent('stop'); } }
只須要給每一個原子操做加上足夠的前提判斷就能夠避免上述問題。it
有時候咱們沒法避免非原子操做,可是咱們要認清哪些是原子操做,不要想固然得認爲上一個原子操做產生的結果必然會是下一個原子操做的環境。在每一個原子操做前加上足夠的判斷。