event loop

javascript event loop事件

1、event loop是什么?

Event Loop 是 JavaScript 异步编程的核心思想。

我们注意到,在异步代码完成后仍有可能要在一旁等待,因为此时程序可能在做其他的事情,等到程序空闲下来才有时间去看哪些异步已经完成了。所以 JavaScript 有一套机制去处理同步和异步操作,那就是事件循环 (Event Loop)。

用文字描述的话,大致是这样的:

  • 所有同步任务都在主线程上执行,形成一个执行栈 (Execution Context Stack)。
  • 而异步任务会被放置到 Task Table,也就是上图中的异步处理模块,当异步任务有了运行结果,就将该函数移入任务队列。
  • 一旦执行栈中的所有同步任务执行完毕,引擎就会读取任务队列,然后将任务队列中的第一个任务压入执行栈中运行。

主线程不断重复第三步,也就是 只要主线程空了,就会去读取任务队列,该过程不断重复,这就是所谓的 事件循环

2、为什么 JavaScript 是单线程的?

我们都知道 JavaScript 是一门 单线程 语言,也就是说同一时间只能做一件事。这是因为 JavaScript 生来作为浏览器脚本语言,主要用来处理与用户的交互、网络以及操作 DOM。这就决定了它只能是单线程的,否则会带来很复杂的同步问题。

假设 JavaScript 有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

既然 Javascript 是单线程的,它就像是只有一个窗口的银行,客户不得不排队一个一个的等待办理。同理 JavaScript 的任务也要一个接一个的执行,如果某个任务(比如加载高清图片)是个耗时任务,那浏览器岂不得一直卡着?为了防止主线程的阻塞,JavaScript 有了 同步异步 的概念。

同步

如果在一个函数返回的时候,调用者就能够得到预期结果,那么这个函数就是同步的。也就是说同步方法调用一旦开始,调用者必须等到该函数调用返回后,才能继续后续的行为。下面这段段代码首先会弹出 alert 框,如果你不点击 确定 按钮,所有的页面交互都被锁死,并且后续的 console 语句不会被打印出来。

alert('Yancey');
console.log('is');
console.log('the');
异步

如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。比如说发一个网络请求,我们告诉主程序等到接收到数据后再通知我,然后我们就可以去做其他的事情了。当异步完成后,会通知到我们,但是此时可能程序正在做其他的事情,所以即使异步完成了也需要在一旁等待,等到程序空闲下来才有时间去看哪些异步已经完成了,再去执行。

这也就是定时器并不能精确在指定时间后输出回调函数结果的原因。

setTimeout(() => {
  console.log('yancey');
}, 1000);

for (let i = 0; i < 100000000; i += 1) {
  // todo
}
3、执行栈和任务队列
执行栈

当我们调用一个方法的时候,JavaScript 会生成一个与这个方法对应的执行环境,又叫执行上下文(context)。这个执行环境中保存着该方法的私有作用域、上层作用域(作用域链)、方法的参数,以及这个作用域中定义的变量和 this 的指向,而当一系列方法被依次调用的时候。由于 JavaScript 是单线程的,这些方法就会按顺序被排列在一个单独的地方,这个地方就是所谓执行栈。

  • 执行栈中的代码永远最先执行
任务队列

事件队列是一个存储着 异步任务 的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。执行栈则是一个类似于函数调用栈的运行容器,当执行栈为空时,JS 引擎便检查事件队列,如果事件队列不为空的话,事件队列便将第一个任务压入执行栈中运行。

  • 执行栈中的代码执行完毕,会在执行宏任务队列之前先看看微任务队列中有没有任务,如果有会先将微任务队列中的任务清空才会去执行宏任务队列
4、面试题利器
  • 在执行时会先进行宏任务,在宏任务如果有setTimeout、fetch、ajax、事件回调、setInterval,setImmediate等会加入消息队列,然后查看有没有微任务,eg. promise、async、await等就先执行微任务,等微任务执行完之后,在进行消息队列的内容
  • Promise.then()相当于每次都返回一个promise对象,但是要执行的事件要进入异步,加入微任务列表
  • Promise.then()函数执行中如果存在setTimeout,会等待定时器进行完成之后在进入下一个进程.then
  • 只有node环境中会存在process.nextTick以及宏任务的setImmediate
  • 一般情况下process.nextTick会先于promise.then执行
  • requestAnimationFrame晚于.then执行,早于消息队列内容执行
  • setTimeout、setImmediate并不能决定哪个优先执行,一般按照压入消息队列的顺序进行执行
  • 对于async、await的理解,async相当于一个promise的excutor函数,同步执行,await相当于promise的.then函数异步执行。
setTimeout(() => {
    console.log('A');  //压入消息队列 //4、A
}, 0);
var obj = {
    func: function() {
        setTimeout(function() {
        	console.log('B');  //压入消息队列  //5、B
    	}, 0);
        return new Promise(function(resolve) {
            console.log('C');  //1、C
            resolve();
        });
	},
};
obj.func().then(function() {
	console.log('D');  //压入微任务队列 //3、D
});
console.log('E');  //2、E
5、面试升级过山车 主要是根据promise进行的微任务分析
得心应手
new Promise((resolve,reject)=>{
    console.log("promise1")  //1、promise1
    resolve()
}).then(()=>{  //压入微任务队列 因为之后没有了 
    console.log("then11")  //2、then11
    new Promise((resolve,reject)=>{
        console.log("promise2")  //3、promise2
        resolve()
    }).then(()=>{
        console.log("then21")  //4、then21
    }).then(()=>{
        console.log("then23")  //6、then23
    })
}).then(()=>{
    console.log("then12")  //5、then12
})
游刃有余

如果.then里面新的promise是被return出去的就有当这个promise执行完才会走下一个微任务

new Promise((resolve,reject)=>{
    console.log("promise1")  //1、promise1
    resolve()
}).then(()=>{
    console.log("then11")  //2、then11
    return new Promise((resolve,reject)=>{
        console.log("promise2")  //3、promise2
        resolve()
	}).then(()=>{
    	console.log("then21")  //4、then21
    }).then(()=>{
    	console.log("then23")  //5、then23
    })
}).then(()=>{
    console.log("then12")  //6、then12 
})
炉火纯青
new Promise((resolve,reject)=>{
    console.log("promise1")  //1、promise1
    resolve()
}).then(()=>{
    console.log("then11")  //3、then11
    new Promise((resolve,reject)=>{
        console.log("promise2")  //4、promise2
        resolve()
    }).then(()=>{
        console.log("then21")  //6、then21
    }).then(()=>{
        console.log("then23")  //8、then23
    })
}).then(()=>{
    console.log("then12")  //7、then12
})
new Promise((resolve,reject)=>{
    console.log("promise3")  //2、promise3
    resolve()
}).then(()=>{
    console.log("then31") //5、then31
})
  • promise1、promise3、then11、promise2、then31、then21、then12、then23
登峰造极

如果.then里面新的promise是被return出去的就有当这个promise执行完才会走下一个微任务

然后会在另一个新的promise走完3步之后再开始进行这个promise之后的下一个任务

new Promise((resolve,reject)=>{
    console.log("promise1")  //1、promise1
    resolve()
}).then(()=>{
	console.log('2')  //3、2
}).then(()=>{
	console.log("then11")  //5、then11
	return new Promise((resolve,reject)=>{
		console.log("promise2")  //6、promise2
		resolve()  
	})
}).then(()=>{
	console.log('3')  //10、3
}).then(()=>{
	console.log('4')  //12、4
})

new Promise((resolve,reject)=>{
	console.log("promise3")  //2、 promise3
	resolve()
}).then(()=>{
	console.log("then31")  //4、then31
}).then(()=>{
	console.log("then33")  //7、then33
}).then(()=>{
	console.log("then34")  //8、then34
}).then(()=>{
	console.log("then35")  //9、then35
}).then(()=>{
	console.log("then37")  //11、then37
}).then(()=>{
	console.log("then39")  //13、then39
})

  • promise1、promise3、 2、then31、 then11、promise2、then33、then34、then35、3、10、then37、4、then39
终极变态

这个是把微任务以及宏任务综合的,过了这一关你就离大神又进了一步

async function async1() {
    console.log("async1 start");  //2、async1 start
    await  async2();  
    console.log("async1 end");   //8、async1 end
}
async  function async2() {
    console.log( 'async2');  //3、async2
}
console.log("script start");   //1、script start
setTimeout(function () {  //消息队列
    console.log("settimeout");  //9、settimeout
});
async1() 
new Promise(function (resolve) {
    console.log("promise1");  //4、promise1
    resolve();
}).then(function () {  
    console.log("promise2");  //7、promise2
});
setImmediate(()=>{  //消息队列
    console.log("setImmediate")  //10、setImmediate
})
process.nextTick(()=>{    
    console.log("process")  //6、process
})
console.log('script end');   //5、script end
  • script start、async1 start、async2、promise1、script end
    • 消息队列 settimeout、setImmediate
    • 微任务队列 async1 end、promise2、process
  • process、promise2、async1 end、settimeout、setImmediate
练习
process.nextTick(function(){  
	console.log('1');  //3、1
})
new Promise(function(resolve){
    console.log('2');  //1、 2
    resolve();
}).then(function(){  
    console.log('3');  //4、3
    setTimeout(function(){
    	console.log('4')  //7、4
    })
})
setTimeout(function(){  //5、5
	console.log('5')
})
new Promise(function(resolve){
    setTimeout(function(){  //6、6
    	console.log('6')
	})
	resolve();
}).then(function(){
    setTimeout(function(){
    	console.log('7')  //8、7
        new Promise(function(resolve){
            setTimeout(function(){
                console.log('8')  //9、 8
            })
            resolve();
        }).then(function(){
            setTimeout(function(){
           		console.log('9')  //10、 9
            })
        })
	})
})
console.log('10')  //2、 10
var o = {
    num:1,
    foo: function(){
        setTimeout(function(){
            console.log('a:', this.num)  //3、undefined
        },0);
        new Promise((resolve,reject) => {
            resolve(2);
        }).then(res =>{
            console.log('b:', res)  //2、2
        })
    	console.log('c:',this.num);  //1、1
    }
}
o.foo();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值