我们都知道JavaScript是单线程的语言,也就是在同一时间只能做一件事,就好比去银行办事,银行确只有一个办事窗口,人们必须一个一个排队,那么为什么要将JavaScript设计成单线程语言呢?我在网上大致看了下,基本上都是这么说的:
JavaScript作为浏览器脚本语言,如果采用多线程的设计方式,会产生一部分复杂的问题,举个例子就是两个线程同时操作一个DOM元素,一个线程是删除这个DOM元素,另一个线程是修改这个DOM,那么要以那个线程为准呢?
大致上都是这样的说法;
然而。。在HTML5中出现了一个API :Web Worker,他允许JavaScript在主线程以外另外创建一个线程,但是这个创建的线程不允许操作DOM元素而且还是完全受控于主线程的,所以这个API实际上并没有改变JavaScript是单线程的这一个特性;
单线程意味着要排队,那么如果遇到了不确定耗时的任务怎么办呢?比如说现在我要发送一个请求,但是这个请求
往返的时间是不确定的,在请求的这段时间,难道要让用户一直等着吗?这对于用户的体验来说是十分不妙的,于是
同步和异步的概念就出来了,那么什么是同步,什么是异步呢?所谓的同步任务就是,老老实实的在主线程中排队执
行的事件;异步任务则是先不执行,而是将任务放入事件队列中,等待同步任务执行完成之后再回过头来执行事件队
列中的异步任务;
大致上的是这样的,比方说我们一起执行两个函数,第一个函数中有一个同步操作与一个异步操作,然后第二个函数中有一个异步操作
let fn1 = function(){
setTimeout( () => {
console.log('函数一:异步');
},0);
console.log('函数一:同步');
},
fn2 = function(){
setTimeout( () => {
console.log('函数二:异步')
},0)
};
fn2();
fn1();
// 输出 '函数一:同步' -> '函数二:异步' -> '函数一:异步'
从这段代码中我们可以看到的是:
在代码中我们先执行的是 fn2 这个函数,但是在这个函数中只有异步的定时器操作,所以JS将这个操作暂时存
入事件队列中,转而去执行了函数 fn1 ,在 fn1 函数中 'console.log('函数一:同步')';这一段是同步操作,
所以就算 setTimeout 定义在前面,JS 也会先将定时器存到事件队列中,去执行 'console.log('函数一:同步')'
这一段操作;也就是说这两个函数执行下来,JS的主线程中是只有 'console.log('函数一:同步')';这一段代码;
而在执行完这段之后,在查看主线程时,JS发现主线程的执行栈中已经没有待执行事件了,就转而去事件队列中将异
步事件,拉入执行栈中执行,由于先执行的 fn2 函数,所以先打印 '函数二:异步',最后打印 '函数一:异步';
然而在异步任务中,又分为了宏任务和微任务两种;
-
宏任务
定时器,AJAX; 这一类任务被定义为宏任务,宏任务的执行顺序是排在了微任务后;
-
微任务
Promise 的 then 就是微任务,但是要注意 new Promise中的操作是同步的操作;
所以最终的执行顺序是 同步 > 异步 ;
异步中: 微任务 > 宏任务;
总的来说: 同步任务 > 微任务 > 宏任务;宏任务与微任务归类到异步任务中;