JS中同步和异步执行顺序(转载)

原文博客地址

1.任务队列

在这里插入图片描述

JavaScript语言是单线程,就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

所有任务可以分为两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

  1. 同步任务指的是:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
  2. 异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

  1. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

  4. 主线程不断重复上面的第三步。

注:setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

任务队列中任务又分为:宏任务和微任务

  1. 宏任务整体代码script、setTimeout、setInterval
  2. 微任务:Promise、process.nextTick

任务队列执行过程:先执行一个宏任务、执行过程中遇到宏任务或微任务,将他们推入到响应的任务队列中、之后在执行微任务、宏任务。如此循环称为事件循环。

在这里插入图片描述

2. 事件和回调函数

  1. 任务队列是一个事件队列(消息队列),主线程读取任务队列,就是读取里面有哪些事件。
  2. 回到函数:指的是会被主线程挂起来的代码,异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
  3. 任务队列是一个先进先出的数据结构,在前面的事件,优先被主线程读取。但是由于定时器功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

3. 通用公式和demo

同步>异步>回调

1.

for (var i = 0; i < 9; i++) {
  setTimeout( function(){
        console.log(i);
  }
  ,1000)
}
console.log(i);
  • for循环先执行,每个i都将setTimeOut回调扔到消息队列中
  • 接着执行后面的同步console.log,此时i=9,到此同步执行完毕,回消息队列执行回调
    9个setTimeOut依次执行,
执行结果:999999999

2.

let a = new Promise(function (resolve, reject) {
  console.log(1);
  
  setTimeout(function () {
    console.log(2)
  },0)
  resolve(true);
}).then(function () {
  console.log(3);
});
console.log(4);
 // 1432
  • a变量是一个Promise,我们知道Promise是异步的,是指它的then()catch()方法,Promise本身还是同步的,所以这里先执行a变量内部的Promise同步代码。(同步优先)所以1先出现。

  • 遇到setTimeOut回调放到消息队列中,resolve(true),调用.then中函数放到消息队列中,最后的console.log是同步操作,所以此时打印4,同步这就执行完了。

  • 异步从消息队列里出来也就是执行.then(),打印3,最后回调也从消息队列出来,打印2

执行结果:1432

3.

    new Promise((resolve, reject) => {    
        setTimeout(() => {
            console.log("1111")
        }, 1000)
        resolve()
    }).then(() => {
        console.log('Hello')
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('2222')
            }, 1000)
            resolve()
        }).then(() => {
            console.log('hello vue')
        })
    })
    new Promise((resolve, reject) =>{
        setTimeout(() => {
            console.log('3333')
        }, 1)
        resolve('Hello World!')
    } ).then((data) => {
        console.log(data)
    })
    console.log('我是同步')

上面这个例子:我们按照写的顺序把上面三个setTimeOut分别叫做S1S2S3, 三个,then叫做T1, T2, T3

  • 遇到S1放到消息对列中,resolve函数调用T1放到消息队列中,遇到S3,放到消息队列中,resolve函数调用T3放到消息队列中。
  • 遇到同步console.log打印我是同步,同步执行完毕。
  • 回到消息队列,此时消息队列中有S1, S3, T1, T3, 按照之前提到的setTimeOut会在任务队列队尾执行,所以先执行T1, 打印Hello
  • 遇到S2放到任务队列中,遇到resolve调用T2放到消息队列中。执行T3 打印Hello World
  • 执行T2,打印hello vue
  • 现在可以执行setTimeOut了,S3延迟1ms执行,所以S3的打印结果3333,然后是S1 打印1111, S2 打印2222

执行结果是:

我是同步
Hello
Hello World!
hello vue
3333
1111
2222

4. 经典题……

	    console.log(1);

        setTimeout(function () {
            console.log('2');
            process.nextTick(function () {
                console.log('3');
            })
            new Promise(function (resolve) {
                console.log('4');
                resolve();
            }).then(function () {
                console.log('5')
            })
        })
        process.nextTick(function () {
            console.log('6');
        })
        new Promise(function (resolve) {
            console.log('7');
            resolve();
        }).then(function () {
            console.log('8')
        })

        setTimeout(function () {
            console.log('9');
            process.nextTick(function () {
                console.log('10');
            })
            new Promise(function (resolve) {
                console.log('11');
                resolve();
            }).then(function () {
                console.log('12')
            })
        })

自己猜下答案吧!

关于promise的详解--------阮一峰

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值