在js中同步与异步的理解分析

一、同步与异步的概念

  1. 单线程:在JS引擎中负责解释和执行JavaScript代码的线程只有一个,一般称它为主线程。但是实际上还存在其他的线程,可以称之为工作线程。JS的单线程能够提高工作效率。JavaScript的主要用途是与用户互动,以及操作DOM,这就决定了它只能是单线程。单线程意味着前一个任务结束,才会执行后一个任务。
  2. 同步:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。如果在函数A返回的时候,调用者就能够得到预期的结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的。
  3. 异步:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。如果在函数A返回的时候,调用者还不能马上得到预期的结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的 。
  4. 异步的过程:主线程发一起一个异步请求,相应的工作线程接收请求并告知主线程已收到通知(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作后,通知主线程;主线程收到通知后,执行一定的动作(调用回调函数)。
  5. 在异步过程中有两个重要的要素,发起函数和回调函数,这两个函数是分离的。
  6. 工作线程在异步操作完成后需要通知主线程,通知机制是工作线程将消息放到消息队列,主线程通过事件循环过程去取消息。
  7. 消息队列:消息队列是一个先进先出的队列,它里面存放这各种消息。
  8. 事件循环:事件循环是指主线程重复从消息队列中取消息,执行的过程。
  9. 同步与异步的区别:同步可以保证顺序一致,但是容易导致阻塞;异步可以解决阻塞问题,但是会改变顺序性。
  10. 任务队列:是一个事件的队列或者消息的队列,所有任务都是在主线程上执行,形成一个执行栈,执行完js主线程的代码才会去看浏览器任务队列中的事件,再执行js代码中该事件对应的代码。任务队列也是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
  11. 异步的执行机制:
    1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
    2)主线程之外,还存在一个"任务队列"(task queue),只要异步任务有了运行结果,就在"任务队列"之中放置一个事件
    3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件,那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
    4)主线程不断重复上面的第三步

二、同步与异步的实例

  1. 同步的实例
    1)代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>同步测试</title>
</head>
<body>

</body>
<script>

        // 同步  执行顺序符合代码的顺序

        console.log("我是第一个div的盒子");
        console.log("我是第二个div的盒子");
        console.log("我是第三个div的盒子");
        console.log("我是第四个div的盒子");
        console.log("我是第五个div的盒子");

</script>
</html>

说明:同步相当于是执行顺序符合代码的顺序,前一个任务执行完毕后才会执行下一个任务。这段程序在控制台中会依次输出 我是第一个div的盒子 我是第一个div的盒子 我是第二个div的盒子
我是第三个div的盒子 我是第四个div的盒子 我是第五个div的盒子 。

  1. 异步的实例
    1)在js当中最常见的异步操作就是setTimeOut()以及setTimeInterval(),它们可以控制js的执行顺序,被压入了称之为Event Loop的队列。
    2)setTimeOut()方法用于在指定的毫秒数后执行某些操作,可以使用clearTimeOut()方法来阻止函数的执行。在指定的毫秒数后,将定时任务处理的函数添加到执行队列的队尾,所以有时候也可以使用setTimeout解决异步带来的问题。
    代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>异步测试</title>
</head>
<body>

</body>
<script>

    // 异步测试    setTimeout() 在指定的毫秒数后执行某些操作

    console.log("我是第一个div盒子");
    setTimeout(function(){
        console.log("我是第二个div盒子");
    },3000);
    console.log("我是第三个div盒子");
    
</script>
</html>

说明:按照代码的执行顺序应该是 我是第一个div盒子 我是第二个div盒子 我是第三个div盒子,但是实际上的输出是 我是第一个div盒子 我是第三个div盒子 我是第二个div盒子。在这里实际上是发生了异步的操作,通过 setTimeout() 函数,设置 我是第二个div盒子 在3秒后执行,添加到执行队列的队尾,让 我是第三个div盒子 先执行,发生异步。

3)setInterval() 方法用于每隔多少时间执行某些操作,可以使用clearInterval()方法来清除定时。按照指定的周期(以毫秒数计时),将定时任务处理函数添加到执行队列的队尾。
代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>异步测试</title>
</head>
<body>

</body>
<script>

  // 异步测试   setInterval() 每隔多少时间执行某操作

    function f3(){
        var test2 = setInterval(f4,5000);
    }
    f3();

    function f4(){
        console.log("我是测试的setInterval()函数");
    }
    
</script>
</html>

说明:在这里也同样是发生异步操作,本来应该是直接输出 我是测试的setInterval()函数 ,但是实际上是过了5秒后才在控制台中输出。通过setInterval() 函数,将f4函数添加到执行队列中,设置在5秒后执行,发生异步操作。

4)setTimeOut()以及setTimeInterval()的区别:
a. setTimeout多少时间之后执行某操作,只执行一次,而setInterval每隔多少时间之后执行某操作,如果不用clearInterval清除的话,将会一直执行下去
b. 两个方法都返回一个id值,用于清除定时器,分别是clearTimeout和clearInterval
c. setTimeout的性能是要优于setInterval的
d. setTimeout和setInterval都不能保证到了时间点一定会执行,这得取决于当前js线程队列里面还有没有其他待处理队列,如果刚好没有的话,那么就能刚好执行,如果当前线程里面已经有了其它待处理队列正在执行,那么需要排队,等到javascript线程空闲的时候才会执行定时器
e. setTimeout是等待循环的操作执行完成之后,才继续在间隔时间之后再把循环操作添加到javascript的线程里面,而setInterval是不等待的,它从来不管放在线程里面循环操作有没有执行完成,反正到点就会把循环操作添加到javascript线程队列里面
f. 能用setInterval实现的操作,一定能用setTimeout来实现
5)Event Loop:一个回调函数队列,当异步函数执行时,回调函数会被压入这个队列。JavaScript引擎直到异步函数执行完成后,才会开始处理事件循环。这意味着JavaScript代码不是多线程的,即使表现的行为相似。事件循环是一个先进先出(FIFO)队列,这说明回调是按照它们被加入队列的顺序执行的。

三、js的异步情况以及异步解决

  1. js的异步情况:
    1)异步函数,比如 setTimeout和setInterval
    2)ajax
    3)node.js中的许多函数也是异步的
  2. 异步问题的解决
    1)命名函数
    清除嵌套回调的一个便捷的解决方案是简单的避免双层以上的嵌套,传递一个命名函数给作为回调参数,而不是传递匿名函数
    代码如下:
var fromLatLng, toLatLng;
 var routeDone = function( e ){
     console.log( "ANNNND FINALLY here's the directions..." );
      // do something with e
  };
  var toAddressDone = function( results, status ) {
      if ( status == "OK" ) {
          toLatLng = results[0].geometry.location;
          map.getRoutes({
             origin: [ fromLatLng.lat(), fromLatLng.lng() ],
             destination: [ toLatLng.lat(), toLatLng.lng() ],
             travelMode: "driving",
             unitSystem: "imperial",
             callback: routeDone
         });
     }
 };
 var fromAddressDone = function( results, status ) {
     if ( status == "OK" ) {
         fromLatLng = results[0].geometry.location;
         GMaps.geocode({
             address: toAddress,
             callback: toAddressDone
         });
     }
 };
 GMaps.geocode({
     address: fromAddress,
     callback: fromAddressDone
 });

2)使用promise
promise在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了
promise还可以做若干个异步的任务
代码如下:

'use strict';
 
 var logging = document.getElementById('test-promise2-log');
 while (logging.children.length > 1) {
     logging.removeChild(logging.children[logging.children.length - 1]);
 }
 
 function log(s) {
     var p = document.createElement('p');
    p.innerHTML = s;
     logging.appendChild(p);
}
 // 0.5秒后返回input*input的计算结果:
 function multiply(input) {
     return new Promise(function (resolve, reject) {
         log('calculating ' + input + ' x ' + input + '...');
        setTimeout(resolve, 500, input * input);
     });
 }
 
 // 0.5秒后返回input+input的计算结果:
 function add(input) {
    return new Promise(function (resolve, reject) {
       log('calculating ' + input + ' + ' + input + '...');
         setTimeout(resolve, 500, input + input);
     });
 }
 
 var p = new Promise(function (resolve, reject) {
    log('start new Promise...');
     resolve(123);
 });

p.then(multiply)
  .then(add)
  .then(multiply)
  .then(add)
  .then(function (result) {
    log('Got value: ' + result);
 });
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值