js的setTimeout和Promise---同步异步和微使命宏使命

久经前端开辟疆场,会阅历形形色色的需求,处置惩罚这些需求时刻,会运用林林总总的api和功用,这里集合对setTimeout和Promise的异步功用举行议论一下。

零丁运用的实行情势

这里就运用Promise作为例子,来探讨一下零丁运用它,会有哪些注重点。

1.最初的探索

实行代码,Promise的基础运用:

let fn = () => {
    console.log(1)
  let a = new Promise((resolve, reject) => {
      console.log(2)
    resolve(3)
  })
  console.log(4)
  return a
}
// 实行
fn().then(data => console.log(data))

以上代码,输出结果为:

1 // 同步
2 // 同步
4 // 同步
3 // 异步

注重 new Promise() 是同步要领,resolve才是异步要领。
另外,上面的要领,能够有下面这类写法,结果同等,主假如把Promise精简了一下:

let fn = () => {
  console.log(1)
  console.log(2)
  let a = Promise.resolve(3)
  console.log(4)
  return a
}

// 实行
fn().then(data => console.log(data))

由于如今议论的是Promise的异步功用,所以下面均运用第二种写法的Promise

2.多个同级Promise

编辑器中,输入以下代码,多个同级的单层的Promise

console.log('同步-0.1')
Promise.resolve().then(() => {
  console.log('P-1.1')
})
Promise.resolve().then(() => {
  console.log('P-1.2')
})
Promise.resolve().then(() => {
  console.log('P-1.3')
})
console.log('同步-0.2')

则会顺次输出以下打印,毫无疑问的结果:

同步-0.1
同步-0.2
P-1.1
P-1.2
P-1.3

3.PromisePromise

庞杂一下,新增行有解释申明:

console.log('同步-0.1')
Promise.resolve().then(() => {
  console.log('P-1.1')
  Promise.resolve().then(() => { // 新加行
    console.log('P-2.1') // 新加行
  }) // 新加行
})
Promise.resolve().then(() => {
  console.log('P-1.2')
  Promise.resolve().then(() => { // 新加行
    console.log('P-2.2') // 新加行
  }) // 新加行
})
Promise.resolve().then(() => {
  console.log('P-1.3')
  Promise.resolve().then(() => { // 新加行
    console.log('P-2.3') // 新加行
  }) // 新加行
})
console.log('同步-0.2')

输出结果以下:

同步-0.1
同步-0.2
P-1.1
P-1.2
P-1.3
P-2.1
P-2.2
P-2.3

可见,多层Promise是一层一层实行的。

4.为了终究确认,举行末了一次考证,在第一个Promise内里多加一层:

console.log('同步-0.1')
Promise.resolve().then(() => {
  console.log('P-1.1')
  Promise.resolve().then(() => {
    console.log('P-2.1')
    Promise.resolve().then(() => { // 新加行
      console.log('P-3.1') // 新加行
    }) // 新加行
    Promise.resolve().then(() => { // 新加行
      console.log('P-3.2') // 新加行
    }) // 新加行
  })
})
Promise.resolve().then(() => {
  console.log('P-1.2')
  Promise.resolve().then(() => {
    console.log('P-2.2')
  })
})
Promise.resolve().then(() => {
  console.log('P-1.3')
  Promise.resolve().then(() => {
    console.log('P-2.3')
  })
})
console.log('同步-0.2')

输出结果以下:

同步-0.1
同步-0.2
P-1.1
P-1.2
P-1.3
P-2.1
P-2.2
P-2.3
P-3.1
P-3.2

确认终了,的确是一层一层的实行。

而且这里能够通知人人,setTimeoutsetInterval在零丁运用的时刻,和Promise是一样的,同样是分层实行,这里不再贴代码了(友谊提示:setInterval的话,须要第一次实行就把这个定时器清掉,不然就无穷实行,卡死页面秒秒钟的事儿),

夹杂运用的实行情势

接下来才是重点,下面将setTimeoutPromise举行夹杂操纵。

console.log('同步-0.1')
Promise.resolve().then(() => {
  console.log('P-1.1')
})
setTimeout(() => {
  console.log('S-1.1')
});
Promise.resolve().then(() => {
  console.log('P-1.2')
})
setTimeout(() => {
  console.log('S-1.2')
});
console.log('同步-0.2')

实行结果以下。。。题目暴露出来了:

同步-0.1
同步-0.2
P-1.1
P-1.2
S-1.1
S-1.2

为何,在同级情况下,是Promise实行完了setTimeout才会实行?

是人道的淹灭,照样品德的沦丧?

是由于JavaScript使命范例!

JavaScript的微使命和宏使命

敲黑板,标重点。

JavaScript的使命分为微使命(Microtasks)和宏使命(task);

  • 宏使命是主流,当js最先被实行的时刻,就是开启一个宏使命,在宏使命中实行一条一条的指令;
  • 宏使命能够同时有多个,但会按递次一个一个实行;
  • 每个宏使命,背面都能够跟一个微使命行列,假如微使命行列中有指令或要领,那末就会实行;假如没有,则最先实行下一个宏使命,直到一切的宏使命实行完为止,微使命相当于宏使命的小尾巴;
  • 为何有了宏使命,还会有微使命存在?由于宏使命太占用机能,当须要一些较早就准备好的要领,排在末了才实行的时刻,又不想新增一个宏使命,那末就能够把这些要领,一个一个的放在微使命行列内里,在这个宏使命中的代码实行完后,就会实行微使命行列。

Promise是微使命,setTimeout是宏使命。

所以上面的代码中,代码实行时会是以下场景:

最先实行当前宏使命代码!

碰到了Promise?好嘞,把它内里的异步代码,放在当前这个宏使命背面微使命内里,然后继承实行咱的;

咦,有个setTimeout?是个宏使命,那在当前这个宏使命背面,建立第二个宏使命,然后把这个setTimeout内里的代码塞进去,咱继承实行;

咦,又一个Promise?把他塞进背面的微使命里。。。什么?已经有代码了?那有啥关联,继承往里塞,放在已有代码的背面就行,咱继承实行;

天啊,又来一个setTimeout,如今背面已经有第二个宏使命了对吧?那就建立第三个宏使命吧,背面再碰到的话,继承建立;

报告!代码实行究竟了,当前这个宏使命实行终了!
行,看一下咱的小尾巴—咱的微使命内里有代码吗?有的话直接实行;

报告,微使命内里,那两个Promise的异步代码实行完了!
干的美丽。。。对了,方才微使命内里,有无新的Promise微使命?有的话,继承在如今这个微使命背面放!对对,只看实行到的代码,有若干放若干,一会儿直接就实行了。。。假如碰到了setTimeout知道该怎么做吧?继承开宏使命!

报告,微使命悉数实行终了!

好!最先实行下一个宏使命!

所以,如今假如实行下面的代码,结果也不言而喻吧:

console.log('同步-0.1')
Promise.resolve().then(() => {
  console.log('P-1.1')
  Promise.resolve().then(() => { // 新加行
    console.log('P-2.1') // 新加行
    Promise.resolve().then(() => { // 新加行
      console.log('P-3.1') // 新加行
    }) // 新加行
  }) // 新加行
})
setTimeout(() => {
  console.log('S-1.1')
});
Promise.resolve().then(() => {
  console.log('P-1.2')
})
setTimeout(() => {
  console.log('S-1.2')
});
console.log('同步-0.2')

实行结果以下:

同步-0.1
同步-0.2
P-1.1
P-1.2
P-2.1
P-3.1
S-1.1
S-1.2

不管Promise套用若干层,都邑鄙人一个setTimeout之前实行。

Dom操纵究竟是同步,照样异步?

这里涌现一个说不清道不明的疑问,Dom操纵究竟是同步操纵照样异步操纵?

假如是同步操纵,那vuenextTick要领是做什么用的?不就是在Dom更新完以后的回调要领吗?

假如是异步操纵,那在猛烈操纵Dom背面的代码,为何会被壅塞?而且代码看上去,也的确是按递次实行的?

这里直接申明:js内里的Dom操纵代码,是同步实行,但浏览器举行的Dom衬着,是异步操纵。

浏览器衬着Dom和实行js,同时只能二选一,衬着一次Dom的机遇是,当前宏使命和小尾巴微使命实行完,下一个宏使命最先前

vue
nextTick要领,则是运用H5的Api—
MutationObserver,监听浏览器将Dom衬着完成的机遇。

若浏览器不支持此要领,则会运用setTimeout,把nextTick回调函数的实行机遇,作为一个宏使命;

上面也说了,浏览器衬着一次Dom,是下一个宏使命最先前,如许运用了setTimeout,保证了Dom确切衬着完成。

这里也须要稍作提示,js操纵Dom是同步的,但操纵Dom,毕竟超出了js自身言语的Api,每操纵一次Dom,都须要斲丧肯定的机能,所以,在合适的情况下,最好先把要修正的Dom的内容,以字符串或许假造Dom的情势拼接好,然后操纵一次Dom,把组装好的Dom字符串或假造Dom,一次性的塞进HTML页面的实在Dom中。

    原文作者:千鸟语
    原文地址: https://segmentfault.com/a/1190000018033021
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞