js执行机制(promise,setTimeout执行顺序)

1.关于javaacript

javascript是一门单线程语言,所以javascript是按语句的执行顺序执行的。
虽然js是单线程,但是我们可以将任务分成两类
1.同步任务:需要执行的任务在主线程上排队,一次执行
2.异步任务:没有立马执行但是需要被执行的任务,放在 任务队列里面,

2.javascript事件循环

当我们打开网站的时候,网页的渲染其实是一堆同步任务,不如页面骨架和页面元素的渲染,但是想图片音乐等占用资源大耗时久的任务就是异步任务,
异步执行:

  • 1.所有同步任务都是在主线程上执行,形成一个很执行栈
  • 2.主线程之外,还存在一个任务队列(task queue)只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
  • 3.一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,就结束等待状态,进入执行栈开始被执行。
  • 4.主线程不断重复以上三步。
    js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
    ![N]M]_N{OL(HBZ`B89REKMQ9.png](https://upload-images.jianshu.io/upload_images/9374643-598ff6c029970ff9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  • 同步和异步分别进入到不同的执行场所,同步进入到主线程,异步的进入到event table 并注册函数
  • 当指定事件完成之后,event table 会将函数移入到event queue
  • 当主线程的任务执行完毕之后,会把event queue里面读取对应的函数进入主线程执行
  • 上述过程会不断循环,形成event loop(事件循环)

3.setTimeout

在使用setTimeout的时候,经常会发现设定的时间与自己设定的时间有差异,贴段代码看一下

setTimeout(() => {
    task();
},3000)
console.log('执行console');

// 执行console 
// task()

上文所说的setTimeout是一个异步的所以会先执行console这个同步任务
但是,如果改成下面这段会发现执行时间远远超过预定的时间

setTimeout(() => {
    task()
},3000)

sleep(10000000)

这是为啥??
我们来看一下是怎么执行的:

  • task()进入到event table里面注册计时
  • 然后主线程执行sleep函数,但是非常慢。计时仍然在继续
  • 3秒到了。task()进入event queue 但是主线程依旧没有走完
  • 终于过了10000000ms之后主线程走完了,task()进入到主线程
    所以可以看出其真实的时间是远远大于3秒的

还会遇到一种情况,就是setTimeout(fn(),0),这样的代码其含义主要是在这个任务会在主线程最早可得的空闲时间执行,换句话说就是主线程的任务执行结束之后立马执行

console.log('先执行这里');
setTimeout(() => {
    console.log('执行啦')
},0);

// 先执行这里
// 执行啦

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。

4.promise 和 process.nextTick(callback)

process.nextTick(callback)类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。

除了广义的同步任务和异步任务,我们可以分的更加精细一点:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick
    不同的任务会进入到不同的event queue。比如setTimeout和setInterval会进入相同的Event Queue。


    事件循环,宏任务,微任务的关系图

    不哔哔,搞段代码瞅瞅:

setTimeout(function() {
    console.log('setTimeout');
})

new Promise(function(resolve) {
    console.log('promise');
    resolve(true)
}).then(function() {
    console.log('then');
})

console.log('console');

// promise
// console
// then
// setTimeout
  • 首先会遇到setTimeout,将其放到宏任务event queue里面
  • 然后回到 promise , new promise 会立即执行, then会分发到微任务
  • 遇到 console 立即执行
  • 整体宏任务执行完成,接下来判断是否有微任务
    ,刚刚放到微任务里面的then,执行
  • ok,第一轮事件结束,进行第二轮,刚刚我们放在event queue 的setTimeout 函数进入到宏任务,立即执行
  • 结束

终于结束了,我们来贴段巨复杂的代码搞一搞

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')
    })
})


// 1,7,6,8,2,4,3,5,9,11,10,12

惊不惊喜,意不意外
我们来分析一下

  • 首先先执行console.log(1) 然后将setTimeout放到宏任务event queue里面 记作 setTimeout 1 ,接着 看到 process.nextTick ,将其放到微任务里面 ,记作 process 1,然后 看到new promise 立即执行输出9 ,将里面的then 放到 微任务里面 记作 then 2, 继续,遇到 setTimeout 放到宏任务里面记作 setTimeout 2 。目前输出的是:1,7,
  • OK, 接下来,开始判断是否有微任务,刚刚放入到微任务event queue的进入到主程序开始执行,process 1 , then 2 目前输出的是:6,8
  • 接下来,微任务的event queue 空了,进行下一轮事件,将刚刚放到宏任务的 setTimeout 1 进入到主线程
    遇到 console 立即执行, 遇到 process.nextTick 放到微任务 event queue 里面 记作 process1, 接着遇到 new Promise 立即执行, 将 then 放到event queue 里面 记作 then 2,OK,当前宏任务里的任务执行完了,判断是否有微任务,发现有 process1, then 2 两个微任务 , 一次执行 目前输出的是:2,4,3,5
  • 目前主线程里的任务都执行结束了,又开始第三轮事件循环,同上(字太多,省略。。。。) 目前输出的是:9,11,10,12

注意: 以上所说只能是在浏览器中的执行顺序,

5.node.js的Event Loop

Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。
node.js运行机制

  • 1.v8引擎解析JavaScript脚本。
  • 2.解析后的代码,调用Node API。
  • 3.libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
  • 4.V8引擎再将结果返回给用户。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容