最近从这个月的博客当中,也不难发现最近掉进了同步和异步的坑里,然后就在这里爬啊爬啊的,爬没爬出来不知道,不过现在有了一些见解分享给大家。并且查资料的过程中,发现阮一峰大神在文档中写到:"上个月,我偶然看到了Philip Roberts的演讲《Help, I'm stuck in an event-loop》。这才尴尬地发现,自己的理解是错的。我决定重写这个题目,详细、完整、正确地描述JavaScript引擎的内部运行机制。下面就是我的重写。" ,我猛的发现连这样的大牛都有理解错的时候,何况是我这样的渣渣,所以更应该准确认真理解。
与这篇相关的文章看我上一篇JavaScript回调函数
先了解一下为什么有异步
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。 如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。 JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。 于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
同步和异步(回调不一定是异步,异步可通过回调实现,注意区分关系)
- 纯JavaScript代码并不是异步的(不使用异步API等)像这样
function A(){
console.log('aaaaa');
}
function B(){
console.log('bbbbb');
}
A();
B();
- 看下边的代码会 先执行函数a,而且不会等到a中的延迟函数执行完才执行函数b, 在延迟函数被触发的过程中就执行了函数b,当js引擎的event 队列空闲时才会去执行队列里等待的setTimeout的回调函数,这就是一个异步的例子。
function a(){
console.log("执行a函数");
setTimeout(function(){
console.log("执行a函数的延迟函数");
},1000);
}
function b(){
console.log("执行b函数");
};
a();
b();
setTimeout 本身就是一个异步操作 ,将回调函数放在一个特殊的"任务队列"中(挂起),只有"任务队列"通知主线程,某个异步任务可以执行了(这里就是延迟一秒到了,主进程空闲下来才会执行这个回调函数),有些人会觉得你本身就是延迟一秒执行啊! 那么我们来看看将延迟时间设为0情况会是什么样?
function a(){
console.log("执行a函数");
setTimeout(function(){
console.log("执行a函数的延迟函数");
},0);
}
function b(){
console.log("执行b函数");
};
a();
b();
举一个别人的栗子 :在公路上,汽车一辆接一辆,有条不紊的运行。这时,有一辆车坏掉了。假如它停在原地进行修理,那么后面的车就会被堵住没法行驶,交通就乱套了。幸好旁边有应急车道,可以把故障车辆推到应急车道修理,而正常的车流不会受到任何影响。等车修好了,再从应急车道回到正常车道即可。唯一的影响就是,应急车道用多了,原来的车辆之间的顺序会有点乱。
我们再来说开始的注意 回调函数不一定 = 异步操作 异步操作可以通过回调来实现 注意两者的定义
- 同步回调
var callback = function(arg3) {
console.log('callback Totle is:' + arg3)
}
function fn(arg1, arg2, callback) {
var Total = arg1 + arg2;
callback(Total);
console.log('mainFunction Totle is:' + Total)
}
fn(2, 2, callback)
- 异步回调
function f2() {
console.log('f2 finished')
}
function f1(cb) {
setTimeout(cb,1000) //用setTimeout()模拟耗时操作
console.log('f1 finished')
}
f1(f2);
这里我们用setTimeout()来模拟耗时操作的前提是js中的setTimeout()函数支持异步处理,所以我们得到的结果是 f1 finished ,f2 finished
- 再来一个node的异步回调
var fs = require("fs");
fs.readFile('input.txt','utf-8', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");
按照javascript单线程应该是先执行fs.readFile函数(读取文件),一直到读完之后 才执行后面的console.log(),然而node中的fs.readFile是支持异步处理的,所以到fs.readFile()不会阻塞,继续向后执行,当读取文件之后在自动调取传入的匿名函数。
说明 :比如node的 fs.readFile()函数是异步的,我们站在巨人的肩膀上进行开发。你要问他为什么的异步的,那只能你研究下node的这个函数是怎么实现的,setTimeout()也一样。 如果有理解不到之处请大牛指出,这样我就能能知道自己哪理解有误了。