JS单线程概念
JS是单线程的,浏览器是多线程的.也就是说浏览器每打开一个页面都是开启了一个进程,一个进程有很多线程.
比如说浏览器加载,加载一个页面遇到一个link,就会开辟一个线程去加载link对应的CSS,但是不会等它.这个同时做多件事就是异步的概念.
但是浏览器加载样式和js是只分配一个线程的.所以说每个CSS和JS文件要执行的程序都是单线程的,而在开发最终大部分都将所有的CSS和所有的JS分别封装成单独的文件了.所有浏览器加载JS和CSS时所给的线程不会很多.执行JS和CSS也都是单线程的
但是JS本身却模拟出了相关的异步操作.比如说定时器. 事件绑定. AJAX中的异步. 回调函数
JS中有两个任务队列,一个是主任务队列,一个是等待任务队列,拿定时器来说,当JS执行到一个定时器的时候.JS会把它放到等待任务队列中等待,然后接着执行下面的代码.等等待时间完成后,JS也不会立即执行等待任务队列里的任务,因为JS是单线程的,所以它要执行完当前任务,再执行任务队列中已经等待完成的事件
JS中所有模拟异步操作都逃不开以上所说的机制,包括AJAX在内也是如此
面试例题
第一题-AJAX异步
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com', true);//=>异步
xhr.onreadystatechange = () => {
if (xhr.readyState === 2) {
console.log(1);
}
if (xhr.readyState === 4) {
console.log(2);
}
};
xhr.send();
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异步
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com', true);
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState === 2) {
console.log(1);
}
if (xhr.readyState === 4) {
console.log(2);
}
};
console.log(3);
输出结果 : 3 1 2
第三题-AJAX同步
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com', false);
xhr.onreadystatechange = () => {
if (xhr.readyState === 2) {
console.log(1);
}
if (xhr.readyState === 4) {
console.log(2);
}
};
xhr.send();
console.log(3);
//=>当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同步
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com', false);
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState === 2) {
console.log(1);
}
if (xhr.readyState === 4) {
console.log(2);
}
};
console.log(3);
输出结果 : 3
代码解析 :
- 第3行 : 此时同步操作,
xhr.send()
在执行的时候AJAX状态在改变,但是此时并未绑定AJAX状态改变事件.当xhr.send()
执行完成之后,AJAX状态已经是4了. - 第4行 : 此时绑定AJAX状态改变事件,但是AJAX状态已经是4了,基本上不会变,所以也不会执行里面的代码块.
- 第12行 : 最终
console.log(3)
输出3
第五题-数据的绑定
按照代码读懂即可
let queryData = function queryData() {
return new Promise(resolve => {
let xhr = new XMLHttpRequest;
xhr.open('GET', `/custom/list?type=chengjiao&_=${Math.random()}`);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)) {
let data = JSON.parse(xhr.responseText);
resolve(data);
}
};
xhr.send(null);
});
};
let bindHTML = function bindHTML() {
let promise = queryData();
promise.then(data => {
//=>完成数据绑定
});
};
bindHTML();
let timer = setInterval(bindHTML, 10000);