Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Q:你了解异步编程、进程、单线程、多线程吗?「终于解决」,希望能够帮助你!!!。
Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
js是单线程的
JS运行在浏览器中,是单线程的,每个window一个JS线程,既然是单线程的,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。而浏览器是事件驱动的,浏览器中很多行为是异步的,会创建事件并放入执行队列中,JavaScript引擎是单线程处理它的任务队列。当异步事件发生时,鼠标点击事件发生、定时器触发事件发生、XMLHttpRequest完成回调触发等,将他们放入执行队列,等待当前代码执行完成。
浏览器不是单线程的
虽然JS运行在浏览器中,是单线程的,但浏览器不是单线程的,例如Web kit引擎,可能有如下线程:
当一个异步事件发生的时候,它就进入事件队列。浏览器有一个内部大消息循环,Event Loop(事件循环),会轮询事件队列并处理事件。比如,浏览器当前正在忙于处理onclick事件,这时window onSize事件发生了,这个异步事件就被放入事件队列等待处理,只有前面的处理完毕了,空闲了才会执行这个事件。
为什么JavaScript是单线程的却能让AJAX异步发送和回调请求,为什么setTimeout也看起来像是多线程的?
Ajax请求确实是异步的,这请求是由浏览器新开了一个线程请求,事件回调的时候是放入Event loop单线程事件队列等候处理。当浏览器空闲的时候出队列任务被处理,JavaScript引擎始终是单线程运行回调函数、单线程处理它的任务队列。
setTimeout(func, 0)
神奇在哪儿?那就是告诉js引擎,在0ms以后把func放到主事件队列中,等待当前的代码执行完毕再执行,注意:重点是改变了代码流程,把func的执行放到了主事件队列中。这就是它的神奇之处了。它的用处有三个:
详细解释见下一篇文章《巧用setTimeout(func, 0)》。(2017-11-30注:本来想写的,偶然翻到一篇文章《这一次,彻底弄懂 JavaScript 执行机制》觉得已经写得很好了,就收藏啦(#^.^#))
这是异步编程最基本的方法。
假定有两个函数f1和f2,后者等待前者的执行结果。
f1();
f2();
如果f1是一个很耗时的任务,可以考虑把f2写成f1的回调函数。
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}
执行代码就变成下面这样:
f1(f2);
采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行。回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。
另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
还是以f1和f2为例。首先,为f1绑定一个事件(这里采用的jQuery的写法)。
f1.on('done', f2);
上面这行代码的意思是,当f1发生done事件,就执行f2。然后,对f1进行改写:
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}
f1.trigger('done')表示,执行完成后,立即触发done事件,从而开始执行f2。
这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
Promise 是异步编程的一种解决方案,比传统的解决方案“回调函数”和“事件”——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
基本用法如下:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
});
下面列出异步操作失败、抓捕异常的另一种写法
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
这样写的优点在于,回调函数变成了链式写法,程序的流程可以看得很清楚,可以实现许多强大的功能。
比如,指定多个回调函数等等。