-
为什么js使用单线程机制
a. 避免复杂性。
试想,如果是多线程,当一个线程正在操作某个dom节点的时候, 另一个线程正试图删除该dom节点,浏览器应该如何判断操作b. HTML5 提出webworker
允许创建多个线程,但子线程受主线程控制,并不可操作dom,本质并没有差异 -
同步 vs 异步
a. 同步
在主线程上执行的任务,必须前一个任务执行完毕,才能执行下一个任务b. 异步
进入任务队列排队的任务。 当异步任务完成,会在队列中添加一个事件【回调函数etc】,表明当前的异步任务可以进入执行栈。 但是只有当执行栈中的所有同步任务完成,系统才会去读取任务队列 -
js里的常见异步操作
a. setTimeout / setInterval
1.由浏览器内核的timer模块来处理,只有当时间到达的时候,才会将回调函数添加到任务队列中 2.需等主线程的所有同步任务完成后才会执行,所以即使定时时间为0也不一定立马执行console.log(1); setTimeout(function(){console.log(2);},0); console.log(3); // 1,3,2b. ajax
由浏览器内核的network模块来处理,在网络请求完成返回之后,才将回调添加至队列c. onClick 事件
由浏览器内核的DOM Binding模块来处理,当事件触发的时候,回调函数立即添加至队列d. promise
详见 --e. nodejs 的 process.nextTick
在下一次Event Loop之前触发回调函数【主线程读取任务队列前】 即在发生异步任务前process.nextTick(function A() { console.log(1); process.nextTick(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0) // 1 // 2 // TIMEOUT FIRED 即使嵌套多个,都会在当前执行栈执行【指定的回调总在当前执行栈尾部触发】f. nodejs 的setImmediate
在当前任务队列的尾部添加事件,在下一次Event Loop时执行setImmediate(function A() { console.log(1); setImmediate(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0); 运行结果可能是1--TIMEOUT FIRED--2,也可能是TIMEOUT FIRED--1--2 setImmediate 总是将事件注册到下一轮Event Loop 将上述例子修改一下 setImmediate(function (){ setImmediate(function A() { console.log(1); setImmediate(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0); }); // 1 // TIMEOUT FIRED // 2 // 运行结果是固定的,以内A和timeout是同一轮loop,二B已经是进入下一轮loop // 推荐使用setImmediate
参考 :
- 阮一峰 《JavaScript 运行机制详解:再谈Event Loop》