JavaScript中同步异步的区别

JavaScript是一门单线程语言,一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
如果一个任务一直在运行,那么后面的任务就需要一直等待,拖延整个程序,可能会造成浏览器无反应,无法继续执行。为了解决这样的问题,js的执行模式分为两种:同步和异步。

同步异步

 同步:停止等待运行结束,继续后续的运行
 异步:就是需要等待一个内容完成后继续执行后面的内容,但是不能将后面的内
 容写在等待函数外,否则就会同时执行两个

其实同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程),同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。

具体来说,异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了
运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",
看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

JavaScript中最简单的异步就是setTimeout和setInterval函数,另外还有两个,分别是load事件和requestAnimationFrame()跳动的帧数。

  1. 异步事件 —— setTimeout 异步(setInterval相同)
    执行的结果是:a->e->d->c->b
    原因是:setTimeout和setInterval,被压入称之为Event Loop的队列
    而且可以改变一个队列函数的执行顺序。这就是说:直到在同一程序段中所有其余的代码执行结束后,(setTimeout和setInterval)超时才会发生。所以如果设置了超时,同时执行了需长时间运行的函数,那么在该函数执行完成之前,超时甚至都不会启动。
        console.log( "a" );
        setTimeout(function() {
            console.log( "b" )
        }, 2000 );
        setTimeout(function() {
            console.log( "c" )
        }, 1000 );
        setTimeout(function() {
            console.log( "d" )
        }, 500 );
        console.log( "e" );
  1. 异步事件 —— load事件
    当窗口内的内容全部加载完成以后,可以获取到图片的位置
    // body部分
    <img src="./img/16-.jpg" id="img1">
    <img src="./img/17.jpg" id="img2">
	// js部分
        window.onload = function () {
            // 当窗口内的内容全部加载完成
            var img1 = document.querySelector("#img1");
            var img2 = document.querySelector("#img2");
            console.log(img1.offsetWidth, img2.offsetWidth)
        }

也可以看下面的这个,获取两个图片的宽度,首先是sum=0;给图片做加载侦听事件,先获取到第一张图片并且获取到宽度,之后加在用同一个方法对第二张图片进行load加载事件,最后输出。这种方法比较麻烦,若要获取许多,不能一个一个写。所有有了下面的方法:

		var sum=0;
        var img=new Image();
        img.src="./img/16-.jpg";
        img.addEventListener("load",loadHandler);

        function loadHandler(e){
            sum+=img.width;
            var img1=new Image();
            img1.src = "./img/17.jpg";
            img1.addEventListener("load",loadHandler1)
        }
        function loadHandler1(e) {
            sum += this.width;
            console.log(sum);
        }

使用map方法,将每一个图片都添加load事件,并且执行同一个函数loadHandler,当都加载完成以后,使用map.set()方法,获取到每个图片的属性。最终通过for…of获取到图片的value值,也就是宽度,并将宽度相加,输出总长度。

        var map = new Map();
        var img1 = new Image();
        img1.src = "./img/16-.jpg";
        img1.addEventListener("load", loadHandler);
        var img2 = new Image();
        img2.src = "./img/17.jpg";
        img2.addEventListener("load", loadHandler);

        function loadHandler(e) {
            map.set(this, this.width);
            console.log(this,this.width);
            if (map.size === 2) {
                var sum = 0;
                for (var value of map.values()) {
                    sum += value;
                }
                console.log(sum);
            }
        }

3异步操作 —— requestAnimationFrame() 屏幕刷新频率
这里介绍一下它两个优势:

  • CPU节能:
    使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
  • 函数节流:
    在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。

这就是自我认识的同步异步的区别,如果哪里不对,不完善的地方,请帮我指出来,谢谢每个阅读的小伙伴。

你可能感兴趣的:(JavaScript)