se7en00 / blog Goto Github PK
View Code? Open in Web Editor NEW前端blog, 关注基础知识
前端blog, 关注基础知识
最近在看到一道面试题时,困惑住了
setTimeout(function() {
console.log(4)
}, 0);
new Promise(function(resolve) {
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve()
}
console.log(2)
}).then(function() {
console.log(5)
}).then(function() {
console.log(6)
});
console.log(3);
out >> 1,2,3,5,6,4
out i think >> 3,4,1,2,5,6
本以为promise跟setTimeout是一样的,多是异步的,JS遇到这些代码,会先把回调函数注册到任务队列中, 等到js主线程执行完同步的代码后(也就是执行完console.log(3))再去执行刚才注册到任务队形的回调函数。但是从结果中可以看出Promise构造函数中的resolve函数的执行先于then的回调函数,then回调函数先于setTimeout回调,而又在console.log(3)后,也就是在JS主线程后。为什么会这样呢!
通过查看一些文章才明白,这些是跟JS event loop有关!上面的script(整体代码)以及其中的setTimeout都属于js event loop中macro-task, 而promise属于micro-task,micro-task的优先级要大于macro-task。
看到这里我相信有部分人会跟我一样懵B了, js event loop, macro-task, micro-task什么东东,没听说过。感觉自己真是too young too nactive! 好吧,废话说了这么多,进入正题~(以下内容仅属个人看法,有误之处希望批评指正)
event loop(事件循环) 根据宿主环境可以分为浏览器下的,workers和nodeJS中的,本文主要是基于浏览器环境的。在这三个环境中,多是只能有一个event loop。
那event loop用来干什么呢?
主要用来实现js的异步机制,js的异步机制简单的说就是不停的循环(JS主线程->先执行同步的代码后->再从任务队列读取事件)的这一过程。
那为什么有这么个event loop呢?
那还不是因为JS一开始就是一门单线程语言,所以对于处理一些耗时比较长的任务,就会阻塞后面的任务,对不对,就像我们想浏览新闻,但是新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来。所以要变个法子来去阻塞化,要异步的去加载那些能阻塞主线程执行的代码。那怎么个异步法呢?
从上图中我们可以了简单描述这个event loop(事件循环):
主线程运行的时候,产生堆(内存分配发生的地方)和栈(函数调用时会形一个个栈帧,其实就是js的执行栈),主线程中同步的代码会进入栈中,当遇到setTimout, promise,用户交互、脚本、UI 渲染、网络请求等各种异步任务时,就将他丢给WebAPIs,接着执行同步任务,直到栈为空。
在此期间WebAPIs完成这个事件,把异步任务中回调函数放入CallbackQueue中等待;
当执行栈为空时,Event Loop把Callback Queue中的一个任务放入栈中,回到第1步。
注:JS的执行栈,就是我们平常所说的执行上下文,包括global执行上下文和function执行上下文, global执行上下文永远在栈底,当我们执行一个function时,会产生一个function执行上下文,放到顶部,然后进入该执行上下文并初始 变量对象, this, 作用域链,初始完后并开始执行代码,遇到function时,重复上面的步骤,当执行环境中的代码执行完毕,退出这个js执行环境并销毁。
这一下子我们对event loop有了个大概的了解。如果你想深入的了解event loop, 这里有几篇深入的文章:
其实上图中script(整体代码),DOM事件,AJAX, setTimout多属于Macro-task。
如果查看了HTML规范,发现Macro-task其实就是event loop里的task, 而HTML规范中的task是这么定义它的:
一个eventloop有一或多个task队列(上图中的第2步的异步任务其实是放到task队列中,wepAPI就是每个task关联的document)
一个task队列是一列有序的task,而且是一个先进先出的队列,主要用来做以下工作:Events task,Parsing task, Callbacks task, Using a resource task, Reacting to DOM manipulation task等
每个task都有自己相关的document,比如我们发起AJAX异步请求时,进入到task队列,这时document就是xmlhttprequest, 其实也就是上图中的webAPI.
每个task由一个确定的task source提供, task source包括如下:
而且从同一个task source来的task必须放到同一个task队列中,从不同源来的则被添加到不同队列中
各个task source的优先级有可能是不一样的,比如会为鼠标键盘事件(用户交互)设置更高优先级,这样会使用户感觉流畅.在、再比如自己遇到一个坑:
//ajax
adminAccountService.getByUserEmail(email, () => {
console.log(1)
});
setTimeout(() => {
console.log(2);
}, 0);
out >> 2, 1
从结果可以看出AJAX的task source的优先级要低于setTimeout的task srouce
有了上面的定义后,当主线程运行到DOM事件,AJAX, setTimeout时,发现你们原来是Macro-task, 不属于我的管辖区内,然后说等你们辖区(各自的web API)处理好你们再来通知道我(回调函数),于是就把他们扔到各自的管辖区内(任务队列)
一般来说,microtask 包括:
Promise 规范中提及 Promise.then 的具体实现由平台把握,可以是 microtask 或 task。当前的共识是使用 microtask 实现。
我们例子中的promise.then其实就是Mico-task.
了解概念性的东西,我们还是去查看HTML规范,从规范中我们可以大概了解到:
每个event loop 多会有一个micro-task队列。Micro-task 会排在 microtask 队列而非 Macro-task 队列中
当当前Macro-task执行完毕后,执行栈为空时(只剩global执行上下文时)会立刻先处理所有Micro-task队列中的任务, 按顺序全部执行,直到队列为空。如果 microtask 中又添加了新的 microtask,直接放进本队列末尾。当micro-task队列多已经执行完后并再去Macro-task队列中取出task去执行...如此反复的循环。Micro-task是跟在Macro-task末尾执行,像个小跟班。
这个要查看ES的规范我们明白下面几点:
Name | Purpose |
---|---|
ScriptJobs | Jobs that validate and evaluate ECMAScript Script and Module source text. See clauses 10 and 15. |
PromiseJobs | PromiseJobs |
通过上面的macro-task, micro-task定义我们知道:
现在我们结合macro,micro task 看看event loop是如何来执行一个任务的流程.
当主线程开始读取完代码(整体代码),执行栈为空时,开始依次执行:
通过这个简单的event loop 执行模型我们在来看最开始的例子:
//步骤1: 开始执行首个eventloop的marco task(整体代码)阶段
setTimeout(function() { //步骤2:立刻将 callback(console.log(4)) 放入 marco task 队列中
console.log(4)
}, 0);
const promise =new Promise(function(resolve) { //步骤3: Promise 构造函数是同步代码,不会放到marco/micro task队列中,输出1
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve() //步骤4:遇到resolve函数此时Promise的状态变成Fulfilled
}
console.log(2) //步骤5: 输出2
})
promise.then(function() { //步骤6: 立刻将 callback(console.log(5)) 放入 microtask 队列中
console.log(5)
}).then(() => {
console.log(6)
});
console.log(3); //步骤7: 输出3
//步骤8: 首个eventloop的 marco task(整体代码)阶段执行完毕,执行栈为空,开始执行 microtask,
发现有一个callback(console.log(5)),执行之,**输出 5**,同时又将callback(console.log(6))放入 microtask 队列中。
//步骤9:发现 microtask 队列不为空,执行callback(console.log(6)),**输出 6**
//步骤10: microtask 队列为空,执行 UI render(根据机器负荷等环境影响,综合浏览器策略,此步骤可能执行也可能不执行)
//步骤11:首次eventloop结束。执行第二轮eventloop,取出一个 marco-task callback(console.log(4)),执行之,**输出 4**
参考:
场景1:最近3个人一起开发同一个task,所以改的代码很容易冲突,每次改完自己的代码要提交时,pull一下,一大堆冲突,很是烦恼。
场景2:一个task快要做完了,但是这时突然来了一个紧急的bug要立马修复后提交,这时我又不想把没做完的提交,怎么办!
git stash:帮助开发人员暂时搁置当前已做的改动,倒退到改动前的状态,进行其他的必要操作(比如发布,或者解决一个bug,或者branch,等等),之后还可以重新载入之前搁置的改动
一般用法: git stash -> git pull -> git stash pop -> git add -> git commit -> git push
一些命令:
git stash [-u]: 搁置当前已做的改动
-u参数 会把untracked的文件也添加到暂存区。
会把我们本地修改的代码暂存到git栈中,再执行git status会发现工作区恢复回去了,返回到HEAD。
git stash list: 查看刚才暂存的版本
这个命令会列出所有的暂存的版本格式为:
stash@{0} (bb0515dfaba4506ae68ce0b6ba301c5dfff9e134)
git stash save 'test-stash'
会增加一个暂存的版本为stash@{0} On develop: 'test-stash'
git stash pop [--index][stash@{id}]: 恢复到先前的工作
选项--index除了恢复工作区的文件外,还尝试恢复暂存区
注意:git stash pop stash@{id}命令会在执行后将对应的stash id 从stash list里删除,而 git stash apply stash@{id} 命令则会继续保存stash id
git stash drop [stash@{id}] : 删除某个stash
如果不加stash编号,默认的就是删除最新的,也就是编号为0的那个
git stash clear: 删除所有stash
git stash show [-p][stash@{id}]: 查看指定的stash的diff
-p参数 表示详细的diff
参考:
#git stash命令 https://www.jianshu.com/p/ea86475cf922
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.