前端面试基础必备JS学习笔记理论汇总

JS同步、异步、阻塞、非阻塞、事件循环、消息队列

2018-08-26  本文已影响312人  puxiaotaoc

一、单线程

二、同步和异步、阻塞和非阻塞

console.log("1");
  setTimeout(function() {
    console.log("2")
  }, 0);
  setTimeout(function() {
    console.log("3")
  }, 0);
  setTimeout(function() {
    console.log("4")
  }, 0);
  console.log("5");
// 1
// 5
// 2
// 3
// 4

异步AJAX:

主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”
AJAX线程:“好的,主线程。我马上去发,但可能要花点儿时间呢,你可以先去忙别的。”
主线程::“谢谢,你拿到响应后告诉我一声啊。”
(接着,主线程做其他事情去了。一顿饭的时间后,它收到了响应到达的通知。)

同步AJAX:

主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”
AJAX线程:“......”
主线程::“喂,AJAX线程,你怎么不说话?”
AJAX线程:“......”
主线程::“喂!喂喂喂!”
AJAX线程:“......”
(一炷香的时间后)
主线程::“喂!求你说句话吧!”
AJAX线程:“主线程,不好意思,我在工作的时候不能说话。你的请求已经发完了,拿到响应数据了,给你。”

      正是由于JavaScript是单线程的,而异步容易实现非阻塞,所以在JavaScript中对于耗时的操作或者时间不确定的操作,使用异步就成了必然的选择;

// 这是一个阻塞式函数, 将一个文件复制到另一个文件上
// 调用这个”copyBigFile()”函数,将一个大文件复制到另一个文件上,将耗时1小时,意味着这个函数的将在一个小时之后返回
function copyBigFile(afile, bfile){
    var result = copyFileSync(afile,bfile);
    return result;
}

//这是一段程序
console.log("start copying ... ");    
var a = copyBigFile('A.txt', 'B.txt');  //这行程序将耗时1小时
console.log("Finished");   // 这行程序将在一小时后执行
console.log("处理一下别的事情");  // 这行程序将在一小时后执行
console.log("Hello World, 整个程序已加载完毕,请享用"); // 这行程序将在一小时后执行
// 这是一个非阻塞式函数
// 如果复制已完成,则返回 true, 如果未完成则返回 false
// 调用这个函数将立刻返回结果
function copyBigFile(afile,bfile){
    var copying = copyFileAsync(afile, bfile);
    var isFinished = !copying;
    return !isFinished; 
}

console.log("start copying ... ");    
// 同步的程序需要在一个循环中轮询结果
while( a = copyBigFile('A.txt', 'B.txt')){
  console.log("在这之间还可以处理别的事情");
} ;  
console.log("Finished");   // 这行程序将在一小时后执行
console.log("Hello World, 整个程序已加载完毕,请享用"); // 这行程序将在一小时后执行

// 非阻塞式的函数给编程带来了更多的便利,在长IO操作的同时,可以写点其他的程序,提高效率,执行结果如下:

// start copying ...
// 在这之间还可以处理别的事情
// 在这之间还可以处理别的事情
// 在这之间还可以处理别的事情
// ...
// Finished
// Hello World, 整个程序已加载完毕,请享用
//非阻塞式的有异步通知能力的函数
//以下不需要看懂,只用知道这个函数会在完成copy操作之后,执行success
function copyBigFile(afile,bfile, callback){
    var copying = copyFileAsync(afile, bfile, function(){ callback();});
    var isFinished = !copying;
    return !isFinished; 
}

// 不同于上一个同步非阻塞函数的地方在于它具有通知功能,能够在完成操作之后主动地通知程序,“我完成了”
console.log("start copying ... ");    
copyBigFile("A.txt","B.txt", function(){
          console.log("Finished");   //一个小时后被执行
          console.log("Hello World, 整个程序已加载完毕,请享用"); //一个小时后被执行
          })
console.log("干别的事情"); 
console.log("做一些别的处理"); 

// 程序在调用copyBigFile函数之后,可以立即获得返回值,线程没有被阻塞住,于是还可以去干些别的事情,然后当copyBigFile完成之后,会执行指定的函数
// start copying ...
// 干别的事情
// 做一些别的处理
// Finished
// Hello World, 整个程序已加载完毕,请享用

三、异步过程的构成要素
      从上文可以看出,异步函数实际上很快就调用完成了,但是后面还有工作线程执行异步任务、通知主线程、主线程调用回调函数等很多步骤,我们把整个过程叫做异步过程,异步函数的调用在整个异步过程中,只是一小部分;
      一个异步过程通常是这样的:主线程发起一个异步请求,相应的工作线程接收请求并告知主线程已收到(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作后,通知主线程;主线程收到通知后,执行一定的动作(调用回调函数);
      异步调用一般分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环的调用,它们属于两个不同的事件循环(tick),彼此没有关联,异步调用一般以传入callback的方式来指定异步操作完成后要执行的动作,而异步调用本体和callback属于不同的事件循环;

try/catch语句只能捕获当次事件循环的异常,对callback无能为力

// 异步函数通常具有以下的形式:
A(args...,callbackFn)
// 它可以叫做异步过程的发起函数,或者叫做异步任务注册函数,args是这个函数需要的参数,callbackFn也是这个函数的参数,但是它比较特殊所以单独列出来;

从主线程的角度看,一个异步过程包括下面两个要素:
1)发起函数(或叫注册函数)A提交请求
2)回调函数callbackFn
它们都是在主线程上调用的,其中注册函数用来发起异步过程,回调函数用来处理结果;

// 举个具体的例子:
setTimeout(fn, 1000);
// 其中的setTimeout就是异步过程的发起函数,fn是回调函数。
// 注意:前面说的形式A(args..., callbackFn)只是一种抽象的表示,并不代表回调函数一定要作为发起函数的参数,例如:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = xxx; // 添加回调函数
xhr.open('GET', url);
xhr.send(); // 发起函数
// 发起函数和回调函数就是分离的。

四、消息队列和事件循环

// 事件循环用代码表示大概是这样的:

while(true) {
    var message = queue.get();
    execute(message);
}
// 再次以异步AJAX为例,假设存在如下的代码:

$.ajax('http://segmentfault.com', function(resp) {
    console.log('我是响应:', resp);
});

// 其他代码
...
...
...
// 消息队列中的消息就长这个样子
var message = function () {
    callbackFn(response);
}

五、异步与事件

// 举例
var button = document.getElement('#btn');
button.addEventListener('click', function(e) {
    console.log();
});
timer.addEventListener('timeout', 1000, fn);

六、生产者与消费者

七、总结

八、Event Loop的其他解释

      可以看到,由于多出了橙色的空闲时间,所以主线程得以运行更多的任务,这就提高了效率。这种运行方式称为"异步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode);

参考链接:
JavaScript:彻底理解同步、异步和事件循环(Event Loop)
js-关于异步原理的理解和总结
js中的同步和异步的个人理解
JS之异步概念
深入解析Javascript异步编程

上一篇下一篇

猜你喜欢

热点阅读