朋友 合同 网站制作,网站地址ip域名查询,seo去哪学,免费网页设计成品下载1、前言
众所周知Javascript是⼀⻔单线程语⾔#xff0c;这也就决定了在Javascript中默认情况下同一个时间只能做一件事情#xff0c;这个特性就造成了js的一些局限性#xff0c;比如我们的页面需要发送HTTP请求从服务器获取数据时#xff0c;就会出现等待数据返回之前页面…1、前言
众所周知Javascript是⼀⻔单线程语⾔这也就决定了在Javascript中默认情况下同一个时间只能做一件事情这个特性就造成了js的一些局限性比如我们的页面需要发送HTTP请求从服务器获取数据时就会出现等待数据返回之前页面假死的效果。由于js在同一个时间只能做一件事就导致了页面的渲染和事件的执行在这个过程中无法同时进行。实际上我们在开发中并没有遇见过这种情况这是为什么呢
为什么js是单线程的呢这主要取决于它的用途js作为浏览器的脚本语言主要用途是处理与用户交互和操作DOM这就决定了它只能是单线程。试想如果js是多线程在一个线程上对DOM节点进行修改另一个线程对该DOM节点进行删除此时浏览器一脸懵圈该以哪个进程为准呢于是为了避免这种情况js从诞生起就决定了它是一门单线程的语言这是它的核心特征。
2、同步(阻塞)和异步(非阻塞)
综上所述在js中应该存在一种解决方案来处理单线程的不足。单线程就意味着所有任务需要排队前一个任务结束才会执行后一个任务。如果前一个任务耗时很长后一个任务就不得不一直等着。
如果排队仅仅是因为计算量大CPU忙不过来倒也算了但是很多时候CPU是空闲的因为IO设备输入输出设备很慢比如Ajax操作从服务器读取数据不得不等着结果出来再往下执行。
JavaScript语言的设计者意识到这时主线程完全可以不管IO设备挂起处于等待中的任务先运行排在后面的任务。等到IO设备返回了结果再回过头把挂起的任务继续执行下去。
于是所有任务可以分成两种一种是同步任务(阻塞)另一种是异步任务(非阻塞)。
同步阻塞
同步任务指的是在主线程上排队执行的任务只有前一个任务执行完毕才能执行后一个任务js会严格按照单线程从上到下、从左到右的方式执行代码逻辑进行代码的解释和运行所以不会出现先执行后面的代码在回头执行前面代码的情况。当我们打开网站时网站的渲染过程比如元素的渲染其实就是一个同步任务。
// 同步代码
const a 1;
const b 2;function fun1() {console.log(3);
}
function fun2() {console.log(4);
}
console.log(a);
fun1();
console.log(b);
fun2();// 输出
// 1
// 3
// 2
// 4很容易可以看出输出会依次输入1,3,2,4因为代码是从上到下依次执行先执行console.log(a)再执行fun1()接着执行console.log(b)最后才执行fun2()。
再看下面的例子
const x 1;
const y 2;
const t1 new Date().getTime();
let t2 new Date().getTime();
while(t2 - t1 3000){t2 new Date().getTime();
}
//这段代码在输出结果之前网页会有一段时间的空白
console.log(xy)解释上面的代码按照顺序从上往下执行时当代码执行到第5行while语句时就会进入一个持续的循环中。因为t1和t2的时间差很微小仅在毫秒内所以while语句的t2-t1的值一定比3000小。每循环一次t2就会获取当前的时间戳值就会发生变化直到t2-t13000时才会跳出当前的循环也就是正好过了3秒的时间最后在输出xy的结果。那么这段代码实际上至少要执行3秒的时间while循环不执行完就不会执行后面的代码这就导致了程序的阻塞这也就是为什么将同步也称为阻塞的原因。
阻塞式运行代码当遇到消耗时间的程序代码时后面的代码都要等前面的代码执行完毕后才能继续执行程序的执行顺序与任务的排列顺序是一致的、同步的。
异步非阻塞
单线程异步异步是和同步对立的异步模式的代码不会按照默认的顺序执行。js仍会严格按照单线程从上到下、从左到右的方式执行代码逻辑进行代码的解释和运行在解释代码时遇到异步任务模式的代码引擎会将当前的任务加入任务队列中(也叫做挂起)先不执行这段代码继续向下执行后面的非异步代码。那么什么时候执行这些异步任务呢直到全部的同步代码执行完毕后进入任务队列的异步任务只有任务队列通知主线程某个异步任务可以执行了该任务才会进入主线程。所以异步代码不会阻塞同步代码的运行并且异步代码加入任务队列并不是进入新的线程同时执行而是等待同步代码执行完毕后再执行。
异步的机制简单的概括为以下四步
1所有同步任务都在主线程上执行形成一个执行栈。
2主线程之外还存在一个任务队列只要异步任务有了结果就会在任务队列中放置一个事件。
3一旦执行栈中的所有同步任务执行完毕系统就会读取任务队列看看里面还有哪些事件哪些对应的异步任务于是结束等待状态进入执行栈开始执行。
4主线程不断地重复上面的第三步。
function fun1() {console.log(1);
}
function fun2() {console.log(2);
}
function fun3() {console.log(3);
}
fun1();
setTimeout(function(){fun2();
},0);
fun3();// 输出
//1
//3
//2这段代码运行时当执行到setTimeout时并不会直接执行函数内部的回调函数fun2()而是会先将内部的函数加入任务队列然后继续执行下面的fun3()fun3()执行完毕后任务队列通知主线程执行fun2()。
⾮阻塞式运⾏的代码程序运⾏到该代码⽚段时执⾏引擎会将程序保存到⼀个任务队列等待所有同步代码全部执⾏完毕后⾮阻塞式的代码会按照特定的执⾏顺序分步执⾏。这就是单线程异步的特点。
3、总结
JavaScript的运⾏顺序就是完全单线程的异步模型同步在前异步在后。所有的异步任务都要等待当前所有的同步任务执⾏完毕之后才能执⾏。
我们把上面的例子改造如下
const x 1;
const y 2;const t1 new Date().getTime();
let t2 new Date().getTime();setTimeout(() {console.log(我是一个异步任务)
}, 1000)while(t2 - t1 3000){t2 new Date().getTime();
}
console.log(xy)// 输出
// 3
// 我是一个异步任务运行这段代码可以看出虽然我们给setTimeout的时间是1000毫秒但是在while的阻塞3000毫秒的循环之后并没有等待1秒钟而是直接输出了结果“我是一个异步任务”因为setTimout的时间计算是从setTimeout()这个函数执⾏时开始计算的。