>[success] # js -- 如何通过事件循环运行异步 现在有一个新问题,这些容器提供的**异步api,如何跟'js 引擎'形成交互的**,这些执行后**异步api回调函数的内容存在哪里,这些回调函数又在什么实际推进'js 引擎'的栈中** 1. 需要容器提供的两个东西'**事件循环和回调队列**' 2. 下图的模型图还不完整,因为在'es6'的时代开始出现了'promise',需要有新的存储位置,这个在后续章节会说明 * 现在得到一个模型图 ![](https://img.kancloud.cn/32/c0/32c0844940c5b2d1aa98a5f03451bccf_700x386.png) >[danger] ##### 回调队列 1. 回调队列也称为 **'消息队列'或'任务队列'** 2. 当代码从上而下的推入'js 引擎'执行栈中的时候,如果该函数**包含Web API调用**,这部分函数**将交给容器对应的webApi线程去执行**,当完成这部分内容规定执行条件时候,例如**定时器所定的时长,xhr请求完成**将这部分**webAPI中的回调函数交给消息队列** **简单**的说在Web API模块中,**异步操作在相应的线程中处理完成得到结果之后,会把结果注入异步回调函数的参数中,并且把回调函数推入回调队列中** * **做一个比喻**当你的 **JavaScript 程序发出了一个 Ajax 请求去服务器获取数据**,你在一个函数(回调)中写了 '**response**' 代码,然后 JS 引擎就会告诉宿主环境**如果你完成了这个网络请求,并且获取到数据的时候,请回来调用这个函数**,浏览器就开始监听什么时候完成,当发现完成了,它将会把回调函数插入到事件循环队列里然后执行。 >[danger] ##### 事件循环 **现在新的问题就是,这些在消息队列中的回调函数什么时候在回到'js引擎'的中?** 1. 这时候就需要每个容器都提供的'**事件循环**',**事件循环的唯一任务是查看回调队列, 一旦回调队列中有东西挂起,并且当'js引擎'栈为空,事件循环一次将一个回调函数推送到栈。** 稍后,'js引擎'栈将执行回调函数 * 注 '**事件循环**'去监控调用栈和回调队列。如果调用栈是空的,它就会取出队列中的第一个事件,然后将它压入到调用栈中,然后运行它 >[danger] ##### 两段代码来看整体效果 [如果你想有更好的体验请看这里](http://latentflip.com/loupe/) ~~~ console.log('Hi'); setTimeout(function cb1() { console.log('cb1'); }, 5000); console.log('Bye'); ~~~ * 如图 ![](https://img.kancloud.cn/5c/5b/5c5b41e37c793ce1fad4f342257bbede_800x600.gif) ~~~ function printHello() { console.log('Hello from baz'); } function baz() { setTimeout(printHello, 3000); } function bar() { baz(); } function foo() { bar(); } foo(); ~~~ * 如图 ![](https://img.kancloud.cn/35/36/3536a28cf44f7aff3d9abaf26cd95539_967x579.gif) * 根据上面图总结**setTimeout(…) 是如何工作的**,`setTimeout(…)`**不会自动的把你的回调放到事件循环队列中**。它设置了一个定时器。**当定时器执行完毕,宿主环境会将你的回调放到事件循环队列中,以便在以后的循环中取走执行它。** ~~~ setTimeout(myCallback, 1000); ~~~ 这并不意味着`myCallback`将会在 1,000ms 之后执行,而是,在 1,000ms 之后将被添加到事件队列。然而,这个队列中可能会拥有一些早一点添加进来的事件 —— 你的回调将会等待被执行 >[info] ## 总结 1. 首先依旧明确 **'js 引擎是单线程而且也没有异步',异步是引擎所在容器提供**,所在**容器会在运行时,提供一些挂载在全局的额外暴露出来的容器api,这些api中他们会被容器中的其他线程代理**,**当引擎执行时候遇到这些容器提供的api**,**js 引擎就会将这些api返回给其对应的线程去执行,当这些线程执行完毕这些内容后,会将这些api里的回调函数,放到'消息队列中','事件循环'去监控调用栈和回调队列。如果调用栈是空的,它就会取出队列中的第一个事件,然后将它压入到调用栈中** 2. **注意的一点'事件循环一次将一个回调函数推送到栈'** 3. 永远只有JS引擎线程在执行JS脚本程序,其他线程只负责将满足触发条件的处理函数推进事件队列,等待JS引擎线程执行。