原子操做这是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
有时候咱们没法避免非原子操做,可是咱们要认清哪些是原子操做,不要想固然得认为上一个原子操做产生的结果必然会是下一个原子操做的环境。在每一个原子操做前加上足够的判断。