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

  • Post author:
  • Post category:其他



原文博客地址



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

分别叫做

S1



S2



S3

, 三个,

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的详解——–阮一峰