JS异步执行之setTimeout 0的妙用

  最近在工作中遇到一些问题,大致是关于js执行问题的。由于没搞清执行顺序,导致出现了一些奇怪的bug。 所以这里整理一些有关异步执行的知识(冰山一角角)...

  大家都知道js是单线程的,执行起来是顺序的,在顺序的业务逻辑中当然没有问题。如果遇到可以并发执行的业务逻辑,再排队就很低级了,所以需要异步执行!

1、什么是异步?

setTimeout(function(){
  console.log(0);
},0)

console.log(1);
// 先打印 1
// 再打印 0

  比方说有些饭店你去吃饭需要提前预定(异步代码执行),等其他人吃完(同步代码执行完毕)你才能去,因此在其他人吃饭的时候(同步代码执行中)你可以去干其他的事情,等其他人吃完了(同步代码执行完毕)会有人来通知你,于是你可以去了(开始执行异步代码【setTimeout/setInterval/事件处理程序/ajax回调...】的执行)。

  我们回到前面那段setTimeout身上,它的工作原理是这样的,当你定义setTimeout那一刻起(不管时间是不是0),js并不会直接去执行这段代码,而是把它扔到一个事件队列里面,当页面中所有同步任务都干完了以后,才会去执行事件队列里面的代码。什么是同步,按代码顺序执行,就像音乐播放器里的顺序播放。

 2、setTimeout 0的妙用

<!DOCTYPE html>
<html>
  <head>
    <title> </title>
    <meta charset="utf-8">
  </head>
  <body>
  <p>
    <input type="text" id="input" value=""/>
    <span id="preview"></span>
  </p>
  </body>
<script type="text/javascript">
(function(){
  function $(id){
    return document.getElementById(id);
  }
  $(‘input‘).onkeypress = function(){
    $(‘preview‘).innerHTML = this.value;
  }
})();
</script>
</html>

  这个keypress函数原意是监听到用户输入字符串就将其完整的显示出来,但是奇怪的是最后一个字符串总是没能显示出来:

  

  重点来了,使用setTimeout 0来解决:

 $(‘input‘).onkeypress = function(){
      setTimeout(function(){ $(‘preview‘).innerHTML = $(‘input‘).value;},0);
  }

  好的,问题就这样解决了,首先回过头来看看问题是如何产生的?

  代码中,我们用到的事件是keypress,而keypress事件发生时,dom元素的状态还未改变,keypress事件之后dom元素的状态才发生改变,通过setTimeout 0延迟执行就能达到期望的结果了。当然,不用setTImeout 0 ,直接用onkeyup亦可。

  但是setTimeout有些小小的问题,就是时间不精确:

<!DOCTYPE html>
<html>
  <head>
    <meta name="generator" content="">
    <title></title>
    <meta charset="utf-8">
  </head>
  <body>
    <h2>未使用 <code>setTimeout</code></h2>
    <button id="makeinput">生成 input</button>
    <p id="inpwrapper"></p>
  </body>
<script type="text/javascript">
(function(){

  function get(id){
    return document.getElementById(id);
  }
  function log(a){
    console.log(a)
  }

  window.onload = function(){
    get(‘makeinput‘).onmousedown=function(){
      var input = document.createElement(‘input‘);
      input.setAttribute(‘type‘, ‘text‘);
      input.setAttribute(‘value‘, ‘test1‘);
      get(‘inpwrapper‘).appendChild(input);
      input.onfocus=function(){    //给生成的input绑定focus事件
         log("iptFocus");
      }
      input.focus();
      log("down");
    }
    get(‘makeinput‘).onfocus = function(){
      log("btnFocus");
    }
    get(‘makeinput‘).onclick = function(){
     log("click");
    }
    get(‘makeinput‘).onmouseup=function(){
      log("up");
    }
   }
})();
</script>
</html>
// 打印 iptFocus
// 打印 down
// 打印 btnFocus 导致新增的input无焦点的真凶
// 打印 up
// 打印 click

  将 input.focus(); 改为 setTimeout(function(){input.focus();},0);得到结果为:

// 打印 down
// 打印 btnFocus
// 打印 iptFocus  鬼知道为什么会跑到这执行
// 打印 up
// 打印 click

  

  理解了js的异步执行和setTImeout 0的工作原理,能更好的方便我们解决工作中的bug...

原文地址:https://www.cnblogs.com/chenwenhao/p/9314025.html

时间: 2024-10-09 01:19:53

JS异步执行之setTimeout 0的妙用的相关文章

浅析JS异步执行机制

前言 JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面.本文将针对JS异步执行机制进行一个简单的分析. 从一份代码讲起 下面是两个经典的JS定时执行函数,这两个函数的区别相信对JS有一定基础的同学是十分清楚的.timeout仅仅只会执行一次,而interval则会执行多次. setTimeout(function (args) { console.log('timeout') }, 1000); setInterval(function (args) { console.log

node js 异步执行流程控制模块Async介绍

1.Async介绍 sync是一个流程控制工具包,提供了直接而强大的异步功能.基于Javascript为Node.js设计,同时也可以直接在浏览器中使用. Async提供了大约20个函数,包括常用的 map, reduce, filter, forEach 等,异步流程控制模式包括,串行(series),并行(parallel),瀑布(waterfall)等. 项目地址:https://github.com/caolan/async 2. Async安装 npm install async 3.

js同步异步执行顺序setTimeOut面试题分析

<script> for(var i=0;i<2;i++){ setTimeout(function(){ console.log(i); },0); } </script> // 结果:2,2 打印两个2而不是0,1,跟js执行顺序有关系. 所有的任务分为两种,一种是同步任务,一种是异步任务.同步任务是指在主线程上排队的任务.异步任务是指不进入主线程.而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异

js异步执行原理

我们都知道js是一个单线程的语言,所以没办法同时执行俩个进程.所以我们就会用到异步. 异步的形式有哪些那,es5的回调函数.es6的promis等 异步的运行原理我们可以先看下面这段代码 应该很多人都知道这个打印的值是10个10. 也有知道当我们执行的for循环的时候,他会把for循环执行完成以后再去执行setTimeout,在执行setTimeout的时候这是时候的i已经变成了10所以就打印10个10. 但是这里面的执行过程是这样的.我们用一个图来做解释 图画的有点丑啊,大致的执行流程就是这样

异步执行js脚本——防止阻塞

JS允许我们修改页面中的所有方面:内容,样式和用户进行交互时的行为. 但是js同样可以阻塞DOM树的形成并且延迟页面的渲染. 让你的js变成异步执行,并且减少不必要的js文件从而提高性能. JavaScript可以查询和修改DOM和CSSOM JavaScript的执行阻塞了CSSOM的执行 JavaScript 阻塞了DOM的形成,除非特殊声明js异步执行 js是一个同步语言可以修改网页的任何方面: <html> <head> <meta name="viewpo

js事件线程机制和异步执行

浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程. javascript执行顺序:http://bbs.html5cn.org/thread-80116-1-1.html 1.javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序. 2.GUI渲染线程负责渲染浏览器界面,当界面需要重绘(R

JS异步处理 setTimeout / setInterval 比较

一般来说JS异步处理主要有两种:  setTimeout 和 setInterval 如果只需要延时执行一次XXX事件,那么一般采用setTimeout setTimeout(function(){alert("kewen nihao!")}, 2000); 很容易明白就是延时2秒,执行方法体里面的代码. 但是如果需要根据某个条件循环执行,那么就必须使用setInterval了. setTimeout在 for/while循环中,是不会等待延时时间的. 比如我现在这样写: for (v

js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环中的同步任务,异步任务: 同步和异步任务在不同的执行"场所",同步的进入主线程,异步的进入Event Table执行并注册函数. 当指定的异步事情完成时,Event Table会将这个函数移入Event Queue. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,推

js异步之间执行的顺序

今天同事问了一个问题,怎么让ajax中的回调(保持异步)先执行,再让普通的function执行... 整了个解决办法: 如下: <!DOCTYPE html> <html> <head> <title>lhy1</title> </head> <body> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"><