Code Monkey home page Code Monkey logo

blog's People

Contributors

se7en00 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

blog's Issues

对event loop的理解

最近在看到一道面试题时,困惑住了

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! 好吧,废话说了这么多,进入正题~(以下内容仅属个人看法,有误之处希望批评指正)

JS Event loop

event loop(事件循环) 根据宿主环境可以分为浏览器下的,workers和nodeJS中的,本文主要是基于浏览器环境的。在这三个环境中,多是只能有一个event loop。

那event loop用来干什么呢?
主要用来实现js的异步机制,js的异步机制简单的说就是不停的循环(JS主线程->先执行同步的代码后->再从任务队列读取事件)的这一过程。

那为什么有这么个event loop呢?
那还不是因为JS一开始就是一门单线程语言,所以对于处理一些耗时比较长的任务,就会阻塞后面的任务,对不对,就像我们想浏览新闻,但是新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来。所以要变个法子来去阻塞化,要异步的去加载那些能阻塞主线程执行的代码。那怎么个异步法呢?

alt text

从上图中我们可以了简单描述这个event loop(事件循环):

  1. 主线程运行的时候,产生堆(内存分配发生的地方)和栈(函数调用时会形一个个栈帧,其实就是js的执行栈),主线程中同步的代码会进入栈中,当遇到setTimout, promise,用户交互、脚本、UI 渲染、网络请求等各种异步任务时,就将他丢给WebAPIs,接着执行同步任务,直到栈为空。

  2. 在此期间WebAPIs完成这个事件,把异步任务中回调函数放入CallbackQueue中等待;

  3. 当执行栈为空时,Event Loop把Callback Queue中的一个任务放入栈中,回到第1步。

    :JS的执行栈,就是我们平常所说的执行上下文,包括global执行上下文和function执行上下文, global执行上下文永远在栈底,当我们执行一个function时,会产生一个function执行上下文,放到顶部,然后进入该执行上下文并初始 变量对象this, 作用域链,初始完后并开始执行代码,遇到function时,重复上面的步骤,当执行环境中的代码执行完毕,退出这个js执行环境并销毁。

这一下子我们对event loop有了个大概的了解。如果你想深入的了解event loop, 这里有几篇深入的文章:

Macro-task

其实上图中script(整体代码),DOM事件,AJAX, setTimout多属于Macro-task。
如果查看了HTML规范,发现Macro-task其实就是event loop里的task, 而HTML规范中的task是这么定义它的:

  1. 一个eventloop有一或多个task队列(上图中的第2步的异步任务其实是放到task队列中,wepAPI就是每个task关联的document

  2. 一个task队列是一列有序的task,而且是一个先进先出的队列,主要用来做以下工作:Events task,Parsing task, Callbacks task, Using a resource task, Reacting to DOM manipulation task等

  3. 每个task都有自己相关的document,比如我们发起AJAX异步请求时,进入到task队列,这时document就是xmlhttprequest, 其实也就是上图中的webAPI.

  4. 每个task由一个确定的task source提供, task source包括如下:

    • DOM 操作任务源:如元素以非阻塞方式插入文档
    • 用户交互任务源:如鼠标键盘事件。用户输入事件(如 click) 必须使用 task 队列
    • 网络任务源:如 XHR 回调
    • history 回溯任务源:使用 history.back() 或者类似 API

    而且从同一个task source来的task必须放到同一个task队列中,从不同源来的则被添加到不同队列中

  5. 各个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)处理好你们再来通知道我(回调函数),于是就把他们扔到各自的管辖区内(任务队列)

Micro-task

一般来说,microtask 包括:

  • Promise.then

    Promise 规范中提及 Promise.then 的具体实现由平台把握,可以是 microtask 或 task。当前的共识是使用 microtask 实现。

  • Object.observe
  • MutationObserver

我们例子中的promise.then其实就是Mico-task.
了解概念性的东西,我们还是去查看HTML规范,从规范中我们可以大概了解到:

  1. 每个event loop 多会有一个micro-task队列。Micro-task 会排在 microtask 队列而非 Macro-task 队列中

  2. 当当前Macro-task执行完毕后,执行栈为空时(只剩global执行上下文时)会立刻先处理所有Micro-task队列中的任务, 按顺序全部执行,直到队列为空。如果 microtask 中又添加了新的 microtask,直接放进本队列末尾。当micro-task队列多已经执行完后并再去Macro-task队列中取出task去执行...如此反复的循环。Micro-task是跟在Macro-task末尾执行,像个小跟班。

为什么只有Promise.then是Micro-task, then中的回调是什么时候进入到Micro-task队列中的呢?而new Promise()构造函数中的resolve和reject函数为什么不是Micro-task呢?

这个要查看ES的规范我们明白下面几点:

  1. 在ES规范中的job就是Micro-task,job队列也是HTML规范中的Micro-task队列, 而ES中的job queue至少包含以下两种Job队列:
Name Purpose
ScriptJobs Jobs that validate and evaluate ECMAScript Script and Module source text. See clauses 10 and 15.
PromiseJobs PromiseJobs
  1. 如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的 [[PromiseFulfill/RejectReactions]] 列表里;只有该promise 被fulfill(resolve)reject 时,其中的[[PromiseFulfill/RejectReactions]]里的每个reactions会创建一个对应的PromiseJob,并被按序通过EnqueueJob 方式进入到Job队列(PromiseJobs)
  2. 如果调用 then 时promise是fulfilled或者rejected,则会直接进入Job队列(PromiseJobs),也就是Micro-task队列。
  3. 把promisJob加入到job队列,规范中只有通过EnqueueJob的方式才会进入job队列,如上面2,3两点中,而且Promise的构造函数中没有该方式, 只有PerformPromiseThen中第8,9步通过该方式加入到job队列中,所以而new Promise()构造函数中的回调是同步的代码。

Event loop的执行模型

通过上面的macro-task, micro-task定义我们知道:

  • macro-task: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
  • micro-task: Promise.then, Object.observe, MutationObserver

现在我们结合macro,micro task 看看event loop是如何来执行一个任务的流程.
当主线程开始读取完代码(整体代码),执行栈为空时,开始依次执行:

  1. 如果 macro-task A 为null (那任务队列就是空),直接跳到第5步
  2. 将 currently running task 设置为 macro-task A
  3. 执行 macro-task A (也就是执行回调函数)
  4. 将 currently running task 设置为 null 并移出 macro-task A
  5. 执行 microtask 队列
    • a:在 microtask 中选出最早的任务 micrtask X
    • b:如果 micrtask X 为null (那 microtask 队列就是空),直接跳到 g
    • c:将 currently running task 设置为 microtask X
    • d:执行 microtask X
    • e:将 currently running task 设置为 null 并移出 microtask X
    • f:在 microtask 中选出最早的任务 , 跳到 b
    • g:结束 microtask 队列
  6. 跳到第一步

通过这个简单的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**

参考:

Git Stash

场景1:最近3个人一起开发同一个task,所以改的代码很容易冲突,每次改完自己的代码要提交时,pull一下,一大堆冲突,很是烦恼。
场景2:一个task快要做完了,但是这时突然来了一个紧急的bug要立马修复后提交,这时我又不想把没做完的提交,怎么办!

git stash

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.