JS单线程概念

JS是单线程的,浏览器是多线程的.也就是说浏览器每打开一个页面都是开启了一个进程,一个进程有很多线程.

比如说浏览器加载,加载一个页面遇到一个link,就会开辟一个线程去加载link对应的CSS,但是不会等它.这个同时做多件事就是异步的概念.

但是浏览器加载样式和js是只分配一个线程的.所以说每个CSS和JS文件要执行的程序都是单线程的,而在开发最终大部分都将所有的CSS和所有的JS分别封装成单独的文件了.所有浏览器加载JS和CSS时所给的线程不会很多.执行JS和CSS也都是单线程的

但是JS本身却模拟出了相关的异步操作.比如说定时器. 事件绑定. AJAX中的异步. 回调函数

JS中有两个任务队列,一个是主任务队列,一个是等待任务队列,拿定时器来说,当JS执行到一个定时器的时候.JS会把它放到等待任务队列中等待,然后接着执行下面的代码.等等待时间完成后,JS也不会立即执行等待任务队列里的任务,因为JS是单线程的,所以它要执行完当前任务,再执行任务队列中已经等待完成的事件

JS中所有模拟异步操作都逃不开以上所说的机制,包括AJAX在内也是如此

面试例题

写出下列代码的输出结果

第一题-AJAX异步

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com', true);//=>异步
  3. xhr.onreadystatechange = () => {
  4. if (xhr.readyState === 2) {
  5. console.log(1);
  6. }
  7. if (xhr.readyState === 4) {
  8. console.log(2);
  9. }
  10. };
  11. xhr.send();
  12. console.log(3);

输出结果 : 3 1 2
代码解析 :

  • 第1行:创建AJAX实例 : xhr
    此时的AJAX状态为 : 0
  • 第2行 : xhr.open(‘GET’, ‘/temp/list’, true);
    这里的true表明xhr是异步操作,所以执行模拟的异步操作后不会等待操作完成才继续走
    此时AJAX的状态为 : 1
  • 第3行 :
    绑定事件 : 这里是模拟异步,所以接着执行下一代码
    监听的是AJAX状态“改变”事件:设置监听之前有一个状态,当后续的状态和设置之前的状态不相同,才会触发这个事件
  • 第11行 :
    发送AJAX请求:这个执行才证明AJAX任务开始
    此时的AJAX状态为 : 1
    只有当响应头被客户端接收才能改变AJAX状态为 : 2 (AJAX状态改变,触发绑定事件,但是这里会立即执行绑定事件,因为JS是单线程,要继续往下执行,待空闲时再执行绑定事件)
  • 第12行 :
    console.log(3)输出 : 3
    此时空闲了,执行等待任务队列里面的绑定事件
    绑定事件 : AJAX状态改变=>2=>响应头被客户端接收=>console.log(1)=>输出1
    绑定事件 : AJAX状态改变=>3=>响应主体内容正在返回=>无事件
    绑定事件 : AJAX状态改变=>4=>响应主体内容已经被客户端接收=>console.log(2)=>输出2
  • 最终结果 : 3 1 2

第二题-AJAX异步

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com', true);
  3. xhr.send();
  4. xhr.onreadystatechange = () => {
  5. if (xhr.readyState === 2) {
  6. console.log(1);
  7. }
  8. if (xhr.readyState === 4) {
  9. console.log(2);
  10. }
  11. };
  12. console.log(3);

输出结果 : 3 1 2

第三题-AJAX同步

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com', false);
  3. xhr.onreadystatechange = () => {
  4. if (xhr.readyState === 2) {
  5. console.log(1);
  6. }
  7. if (xhr.readyState === 4) {
  8. console.log(2);
  9. }
  10. };
  11. xhr.send();
  12. console.log(3);
  13. //=>当AJAX任务开始,由于是同步编程,主任务队列在状态没有变成4(任务结束)之前一直被这件事占用着,其它事情都做不了(当服务器把响应头返回的时候,状态为2,触发了事件readystatechange,但是由于主任务队列没有完成,被占着呢,绑定的方法也无法执行... 所有只有状态为4的时候执行一次这个方法)

输出结果 : 2 3
代码解析 :

  • 第2行 : 这里的定义同步操作
  • 第11行 : 任务开始(同步:只要当前AJAX请求这件事没有完成,什么都不能做)要等待响应主体完全被客户端接收才能继续执行下一行代码
    绑定事件 : AJAX状态改变=>2=>响应头被客户端接收=>此时代码xhr.send();还未执行完,占用着JS线程
    绑定事件 : AJAX状态改变=>3=>响应主体内容正在返回=>无事件
    绑定事件 : AJAX状态改变=>4=>响应主体内容已经被客户端接收=>此时代码xhr.send();执行完成,JS线程释放=>执行绑定事件console.log(2)=>输出2
  • 第12行 : 最终console.log(3)输出3

第四题-AJAX同步

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com', false);
  3. xhr.send();
  4. xhr.onreadystatechange = () => {
  5. if (xhr.readyState === 2) {
  6. console.log(1);
  7. }
  8. if (xhr.readyState === 4) {
  9. console.log(2);
  10. }
  11. };
  12. console.log(3);

输出结果 : 3
代码解析 :

  • 第3行 : 此时同步操作,xhr.send()在执行的时候AJAX状态在改变,但是此时并未绑定AJAX状态改变事件.当xhr.send()执行完成之后,AJAX状态已经是4了.
  • 第4行 : 此时绑定AJAX状态改变事件,但是AJAX状态已经是4了,基本上不会变,所以也不会执行里面的代码块.
  • 第12行 : 最终console.log(3)输出3

第五题-数据的绑定

按照代码读懂即可

  1. let queryData = function queryData() {
  2. return new Promise(resolve => {
  3. let xhr = new XMLHttpRequest;
  4. xhr.open('GET', `/custom/list?type=chengjiao&_=${Math.random()}`);
  5. xhr.onreadystatechange = () => {
  6. if (xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)) {
  7. let data = JSON.parse(xhr.responseText);
  8. resolve(data);
  9. }
  10. };
  11. xhr.send(null);
  12. });
  13. };
  14. let bindHTML = function bindHTML() {
  15. let promise = queryData();
  16. promise.then(data => {
  17. //=>完成数据绑定
  18. });
  19. };
  20. bindHTML();
  21. let timer = setInterval(bindHTML, 10000);