成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

最后一次搞懂 Event Loop

gself / 3528人閱讀

摘要:由于是單線程的,這些方法就會(huì)按順序被排列在一個(gè)多帶帶的地方,這個(gè)地方就是所謂執(zhí)行棧。事件隊(duì)列每次僅執(zhí)行一個(gè)任務(wù),在該任務(wù)執(zhí)行完畢之后,再執(zhí)行下一個(gè)任務(wù)。

Event Loop 是 JavaScript 異步編程的核心思想,也是前端進(jìn)階必須跨越的一關(guān)。同時(shí),它又是面試的必考點(diǎn),特別是在 Promise 出現(xiàn)之后,各種各樣的面試題層出不窮,花樣百出。這篇文章從現(xiàn)實(shí)生活中的例子入手,讓你徹底理解 Event Loop 的原理和機(jī)制,并能游刃有余的解決此類面試題。

宇宙條那道爛大街的筆試題鎮(zhèn)樓
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
console.log("script start");
setTimeout(function() {
  console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
console.log("script end");
為什么 JavaScript 是單線程的?

我們都知道 JavaScript 是一門 單線程 語(yǔ)言,也就是說同一時(shí)間只能做一件事。這是因?yàn)?JavaScript 生來作為瀏覽器腳本語(yǔ)言,主要用來處理與用戶的交互、網(wǎng)絡(luò)以及操作 DOM。這就決定了它只能是單線程的,否則會(huì)帶來很復(fù)雜的同步問題。

假設(shè) JavaScript 有兩個(gè)線程,一個(gè)線程在某個(gè) DOM 節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?

既然 Javascript 是單線程的,它就像是只有一個(gè)窗口的銀行,客戶不得不排隊(duì)一個(gè)一個(gè)的等待辦理。同理 JavaScript 的任務(wù)也要一個(gè)接一個(gè)的執(zhí)行,如果某個(gè)任務(wù)(比如加載高清圖片)是個(gè)耗時(shí)任務(wù),那瀏覽器豈不得一直卡著?為了防止主線程的阻塞,JavaScript 有了 同步異步 的概念。

同步和異步 同步

如果在一個(gè)函數(shù)返回的時(shí)候,調(diào)用者就能夠得到預(yù)期結(jié)果,那么這個(gè)函數(shù)就是同步的。也就是說同步方法調(diào)用一旦開始,調(diào)用者必須等到該函數(shù)調(diào)用返回后,才能繼續(xù)后續(xù)的行為。下面這段段代碼首先會(huì)彈出 alert 框,如果你不點(diǎn)擊 確定 按鈕,所有的頁(yè)面交互都被鎖死,并且后續(xù)的 console 語(yǔ)句不會(huì)被打印出來。

alert("Yancey");
console.log("is");
console.log("the");
console.log("best");
異步

如果在函數(shù)返回的時(shí)候,調(diào)用者還不能夠得到預(yù)期結(jié)果,而是需要在將來通過一定的手段得到,那么這個(gè)函數(shù)就是異步的。比如說發(fā)一個(gè)網(wǎng)絡(luò)請(qǐng)求,我們告訴主程序等到接收到數(shù)據(jù)后再通知我,然后我們就可以去做其他的事情了。當(dāng)異步完成后,會(huì)通知到我們,但是此時(shí)可能程序正在做其他的事情,所以即使異步完成了也需要在一旁等待,等到程序空閑下來才有時(shí)間去看哪些異步已經(jīng)完成了,再去執(zhí)行。

這也就是定時(shí)器并不能精確在指定時(shí)間后輸出回調(diào)函數(shù)結(jié)果的原因。

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

for (let i = 0; i < 100000000; i += 1) {
  // todo
}
執(zhí)行棧和任務(wù)隊(duì)列 復(fù)習(xí)下數(shù)據(jù)結(jié)構(gòu)吧

棧 (stack): 棧是遵循后進(jìn)先出 (LIFO) 原則的有序集合,新添加或待刪除的元素都保存在同一端,稱為棧頂,另一端叫做棧底。在棧里,新元素都靠近棧頂,舊元素都接近棧底。棧在編程語(yǔ)言的編譯器和內(nèi)存中存儲(chǔ)基本數(shù)據(jù)類型和對(duì)象的指針、方法調(diào)用等.

隊(duì)列 (queue): 隊(duì)列是遵循先進(jìn)先出 (FIFO) 原則的有序集合,隊(duì)列在尾部添加新元素,并在頂部移除元素,最新添加的元素必須排在隊(duì)列的末尾。在計(jì)算機(jī)科學(xué)中,最常見的例子就是打印隊(duì)列。

堆 (heap): 堆是基于樹抽象數(shù)據(jù)類型的一種特殊的數(shù)據(jù)結(jié)構(gòu)。

如上圖所示,JavaScript 中的內(nèi)存分為 堆內(nèi)存棧內(nèi)存,

JavaScript 中引用類型值的大小是不固定的,因此它們會(huì)被存儲(chǔ)到 堆內(nèi)存 中,由系統(tǒng)自動(dòng)分配存儲(chǔ)空間。JavaScript 不允許直接訪問堆內(nèi)存中的位置,因此我們不能直接操作對(duì)象的堆內(nèi)存空間,而是操作 對(duì)象的引用

而 JavaScript 中的基礎(chǔ)數(shù)據(jù)類型都有固定的大小,因此它們被存儲(chǔ)到 棧內(nèi)存 中。我們可以直接操作保存在棧內(nèi)存空間的值,因此基礎(chǔ)數(shù)據(jù)類型都是 按值訪問。此外,棧內(nèi)存還會(huì)存儲(chǔ) 對(duì)象的引用 (指針) 以及 函數(shù)執(zhí)行時(shí)的運(yùn)行空間。

下面比較一下兩種存儲(chǔ)方式的不同。

棧內(nèi)存 堆內(nèi)存
存儲(chǔ)基礎(chǔ)數(shù)據(jù)類型 存儲(chǔ)引用數(shù)據(jù)類型
按值訪問 按引用訪問
存儲(chǔ)的值大小固定 存儲(chǔ)的值大小不定,可動(dòng)態(tài)調(diào)整
由系統(tǒng)自動(dòng)分配內(nèi)存空間 由程序員通過代碼進(jìn)行分配
主要用來執(zhí)行程序 主要用來存放對(duì)象
空間小,運(yùn)行效率高 空間大,但是運(yùn)行效率相對(duì)較低
先進(jìn)后出,后進(jìn)先出 無序存儲(chǔ),可根據(jù)引用直接獲取
執(zhí)行棧

當(dāng)我們調(diào)用一個(gè)方法的時(shí)候,JavaScript 會(huì)生成一個(gè)與這個(gè)方法對(duì)應(yīng)的執(zhí)行環(huán)境,又叫執(zhí)行上下文(context)。這個(gè)執(zhí)行環(huán)境中保存著該方法的私有作用域、上層作用域(作用域鏈)、方法的參數(shù),以及這個(gè)作用域中定義的變量和 this 的指向,而當(dāng)一系列方法被依次調(diào)用的時(shí)候。由于 JavaScript 是單線程的,這些方法就會(huì)按順序被排列在一個(gè)多帶帶的地方,這個(gè)地方就是所謂執(zhí)行棧。

任務(wù)隊(duì)列

事件隊(duì)列是一個(gè)存儲(chǔ)著 異步任務(wù) 的隊(duì)列,其中的任務(wù)嚴(yán)格按照時(shí)間先后順序執(zhí)行,排在隊(duì)頭的任務(wù)將會(huì)率先執(zhí)行,而排在隊(duì)尾的任務(wù)會(huì)最后執(zhí)行。事件隊(duì)列每次僅執(zhí)行一個(gè)任務(wù),在該任務(wù)執(zhí)行完畢之后,再執(zhí)行下一個(gè)任務(wù)。執(zhí)行棧則是一個(gè)類似于函數(shù)調(diào)用棧的運(yùn)行容器,當(dāng)執(zhí)行棧為空時(shí),JS 引擎便檢查事件隊(duì)列,如果事件隊(duì)列不為空的話,事件隊(duì)列便將第一個(gè)任務(wù)壓入執(zhí)行棧中運(yùn)行。

事件循環(huán)

我們注意到,在異步代碼完成后仍有可能要在一旁等待,因?yàn)榇藭r(shí)程序可能在做其他的事情,等到程序空閑下來才有時(shí)間去看哪些異步已經(jīng)完成了。所以 JavaScript 有一套機(jī)制去處理同步和異步操作,那就是事件循環(huán) (Event Loop)。

下面就是事件循環(huán)的示意圖。

用文字描述的話,大致是這樣的:

所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧 (Execution Context Stack)。

而異步任務(wù)會(huì)被放置到 Task Table,也就是上圖中的異步處理模塊,當(dāng)異步任務(wù)有了運(yùn)行結(jié)果,就將該函數(shù)移入任務(wù)隊(duì)列。

一旦執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢,引擎就會(huì)讀取任務(wù)隊(duì)列,然后將任務(wù)隊(duì)列中的第一個(gè)任務(wù)壓入執(zhí)行棧中運(yùn)行。

主線程不斷重復(fù)第三步,也就是 只要主線程空了,就會(huì)去讀取任務(wù)隊(duì)列,該過程不斷重復(fù),這就是所謂的 事件循環(huán)。

宏任務(wù)和微任務(wù)

微任務(wù)、宏任務(wù)與 Event-Loop 這篇文章用了很有趣的例子來解釋宏任務(wù)和微任務(wù),下面 copy 一下。

還是以去銀行辦業(yè)務(wù)為例,當(dāng) 5 號(hào)窗口柜員處理完當(dāng)前客戶后,開始叫號(hào)來接待下一位客戶,我們將每個(gè)客戶比作 宏任務(wù)接待下一位客戶 的過程也就是讓下一個(gè) 宏任務(wù) 進(jìn)入到執(zhí)行棧。

所以該窗口所有的客戶都被放入了一個(gè) 任務(wù)隊(duì)列 中。任務(wù)隊(duì)列中的都是 已經(jīng)完成的異步操作的,而不是注冊(cè)一個(gè)異步任務(wù)就會(huì)被放在這個(gè)任務(wù)隊(duì)列中(它會(huì)被放到 Task Table 中)。就像在銀行中排號(hào),如果叫到你的時(shí)候你不在,那么你當(dāng)前的號(hào)牌就作廢了,柜員會(huì)選擇直接跳過進(jìn)行下一個(gè)客戶的業(yè)務(wù)處理,等你回來以后還需要重新取號(hào)。

在執(zhí)行宏任務(wù)時(shí),是可以穿插一些微任務(wù)進(jìn)去。比如你大爺在辦完業(yè)務(wù)之后,順便問了下柜員:“最近 P2P 暴雷很嚴(yán)重啊,有沒有其他穩(wěn)妥的投資方式”。柜員暗爽:“又有傻子上鉤了”,然后嘰里咕嚕說了一堆。

我們分析一下這個(gè)過程,雖然大爺已經(jīng)辦完正常的業(yè)務(wù),但又咨詢了一下理財(cái)信息,這時(shí)候柜員肯定不能說:“您再上后邊取個(gè)號(hào)去,重新排隊(duì)”。所以只要是柜員能夠處理的,都會(huì)在響應(yīng)下一個(gè)宏任務(wù)之前來做,我們可以把這些任務(wù)理解成是 微任務(wù)

大爺聽罷,揚(yáng)起 45 度微笑,說:“我就問問?!?/p>

柜員 OS:“艸...”

這個(gè)例子就說明了:你大爺永遠(yuǎn)是你大爺 在當(dāng)前微任務(wù)沒有執(zhí)行完成時(shí),是不會(huì)執(zhí)行下一個(gè)宏任務(wù)的!

總結(jié)一下,異步任務(wù)分為 宏任務(wù)(macrotask)微任務(wù) (microtask)。宏任務(wù)會(huì)進(jìn)入一個(gè)隊(duì)列,而微任務(wù)會(huì)進(jìn)入到另一個(gè)不同的隊(duì)列,且微任務(wù)要優(yōu)于宏任務(wù)執(zhí)行。

常見的宏任務(wù)和微任務(wù)

宏任務(wù):script(整體代碼)、setTimeout、setInterval、I/O、事件、postMessage、 MessageChannel、setImmediate (Node.js)

微任務(wù):Promise.then、 MutaionObserver、process.nextTick (Node.js)

來做幾道題

看看下面這道題你能不能做出來。

setTimeout(() => {
  console.log("A");
}, 0);
var obj = {
  func: function() {
    setTimeout(function() {
      console.log("B");
    }, 0);
    return new Promise(function(resolve) {
      console.log("C");
      resolve();
    });
  },
};
obj.func().then(function() {
  console.log("D");
});
console.log("E");

第一個(gè) setTimeout 放到宏任務(wù)隊(duì)列,此時(shí)宏任務(wù)隊(duì)列為 ["A"]

接著執(zhí)行 obj 的 func 方法,將 setTimeout 放到宏任務(wù)隊(duì)列,此時(shí)宏任務(wù)隊(duì)列為 ["A", "B"]

函數(shù)返回一個(gè) Promise,因?yàn)檫@是一個(gè)同步操作,所以先打印出 "C"

接著將 then 放到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["D"]

接著執(zhí)行同步任務(wù) console.log("E");,打印出 "E"

因?yàn)槲⑷蝿?wù)優(yōu)先執(zhí)行,所以先輸出 "D"

最后依次輸出 "A""B"

再來看一道阮一峰老師出的題目,其實(shí)也不難。

let p = new Promise(resolve => {
  resolve(1);
  Promise.resolve().then(() => console.log(2));
  console.log(4);
}).then(t => console.log(t));
console.log(3);

首先將 Promise.resolve() 的 then() 方法放到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["2"]

然后打印出同步任務(wù) 4

接著將 p 的 then() 方法放到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["2", "1"]

打印出同步任務(wù) 3

最后依次打印微任務(wù) 21

當(dāng) Event Loop 遇到 async/await

我們知道,async/await 僅僅是生成器的語(yǔ)法糖,所以不要怕,只要把它轉(zhuǎn)換成 Promise 的形式即可。下面這段代碼是 async/await 函數(shù)的經(jīng)典形式。

async function foo() {
  // await 前面的代碼
  await bar();
  // await 后面的代碼
}

async function bar() {
  // do something...
}

foo();

其中 await 前面的代碼 是同步的,調(diào)用此函數(shù)時(shí)會(huì)直接執(zhí)行;而 await bar(); 這句可以被轉(zhuǎn)換成 Promise.resolve(bar());await 后面的代碼 則會(huì)被放到 Promise 的 then() 方法里。因此上面的代碼可以被轉(zhuǎn)換成如下形式,這樣是不是就很清晰了?

function foo() {
  // await 前面的代碼
  Promise.resolve(bar()).then(() => {
    // await 后面的代碼
  });
}

function bar() {
  // do something...
}

foo();

回到開篇宇宙條那道爛大街的題目,我們"重構(gòu)"一下代碼,再做解析,是不是很輕松了?

function async1() {
  console.log("async1 start"); // 2

  Promise.resolve(async2()).then(() => {
    console.log("async1 end"); // 6
  });
}

function async2() {
  console.log("async2"); // 3
}

console.log("script start"); // 1

setTimeout(function() {
  console.log("settimeout"); // 8
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1"); // 4
  resolve();
}).then(function() {
  console.log("promise2"); // 7
});
console.log("script end"); // 5

首先打印出 script start

接著將 settimeout 添加到宏任務(wù)隊(duì)列,此時(shí)宏任務(wù)隊(duì)列為 ["settimeout"]

然后執(zhí)行函數(shù) async1,先打印出 async1 start,又因?yàn)?Promise.resolve(async2()) 是同步任務(wù),所以打印出 async2,接著將 async1 end 添加到微任務(wù)隊(duì)列,,此時(shí)微任務(wù)隊(duì)列為 ["async1 end"]

接著打印出 promise1,將 promise2 添加到微任務(wù)隊(duì)列,,此時(shí)微任務(wù)隊(duì)列為 ["async1 end", promise2]

打印出 script end

因?yàn)槲⑷蝿?wù)優(yōu)先級(jí)高于宏任務(wù),所以先依次打印出 async1 endpromise2

最后打印出宏任務(wù) settimeout

關(guān)于這道題的爭(zhēng)議:文章發(fā)表了大概有兩天的時(shí)間,陸陸續(xù)續(xù)收到了小伙伴的評(píng)論。大多都是 async1 endpromise2 的順序問題。我在 Chrome 73.0.3683.103 for MACNode.js v8.15.1 測(cè)試是 async1 end 先于 promise2,在 FireFox 66.0.3 for MAC 測(cè)試是 async1 end 后于 promise2

Node.js 與 瀏覽器環(huán)境下事件循環(huán)的區(qū)別

Node.js 在升級(jí)到 11.x 后,Event Loop 運(yùn)行原理發(fā)生了變化,一旦執(zhí)行一個(gè)階段里的一個(gè)宏任務(wù)(setTimeout,setInterval 和 setImmediate) 就立刻執(zhí)行微任務(wù)隊(duì)列,這點(diǎn)就跟瀏覽器端一致。

關(guān)于 11.x 版本之前 Node.js 與 瀏覽器環(huán)境下事件循環(huán)的區(qū)別,可以參考 @浪里行舟 大佬的 《瀏覽器與 Node 的事件循環(huán)(Event Loop)有何區(qū)別"); 淺談 Web Workers

需要強(qiáng)調(diào)的是,Worker 是瀏覽器 (即宿主環(huán)境) 的功能,實(shí)際上和 JavaScript 語(yǔ)言本身幾乎沒有什么關(guān)系。也就是說,JavaScript 當(dāng)前并沒有任何支持多線程執(zhí)行的功能。

所以,JavaScript 是一門單線程的語(yǔ)言!JavaScript 是一門單線程的語(yǔ)言!JavaScript 是一門單線程的語(yǔ)言!

瀏覽器可以提供多個(gè) JavaScript 引擎實(shí)例,各自運(yùn)行在自己的線程上,這樣你可以在每個(gè)線程上運(yùn)行不同的程序。程序中每一個(gè)這樣的的獨(dú)立的多線程部分被稱為一個(gè) Worker。這種類型的并行化被稱為 任務(wù)并行,因?yàn)槠渲攸c(diǎn)在于把程序劃分為多個(gè)塊來并發(fā)運(yùn)行。下面是 Worker 的運(yùn)作流圖。

Web Worker 實(shí)例

下面用一個(gè)階乘的例子淺談 Worker 的用法。

首先新建一個(gè) index.html ,直接上代碼:

<body>
  <fieldset>
    <legend>計(jì)算階乘legend>
    <input id="input" type="number" placeholder="請(qǐng)輸入一個(gè)正整數(shù)" />
    <button id="btn">計(jì)算button>
    <p>計(jì)算結(jié)果:<span id="result">span>p>
  fieldset>
  <legend>legend>

  <script>
    const input = document.getElementById("input");
    const btn = document.getElementById("btn");
    const result = document.getElementById("result");

    btn.addEventListener("click", () => {
      const worker = new Worker("./worker.js");

      // 向 Worker 發(fā)送消息
      worker.postMessage(input.value);

      // 接收來自 Worker 的消息
      worker.addEventListener("message", e => {
        result.innerHTML = e.data;

        // 使用完 Worker 后記得關(guān)閉
        worker.terminate();
      });
    });
  script>
body>

在同目錄下新建一個(gè) work.js,內(nèi)容如下:

function memorize(f) {
  const cache = {};
  return function() {
    const key = Array.prototype.join.call(arguments, ",");
    if (key in cache) {
      return cache[key];
    } else {
      return (cache[key] = f.apply(this, arguments));
    }
  };
}

const factorial = memorize(n => {
  return n <= 1 ");1 : n * factorial(n - 1);
});

// 監(jiān)聽主線程發(fā)過來的消息
self.addEventListener(
  "message",
  function(e) {
    // 響應(yīng)主線程
    self.postMessage(factorial(e.data));
  },
  false,
);
以兩道題收尾

下面的兩道題來自 @小美娜娜 的文章 Eventloop 不可怕,可怕的是遇上 Promise。抄一下不會(huì)打我吧,嗯。

第一道題
const p1 = new Promise((resolve, reject) => {
  console.log("promise1");
  resolve();
})
  .then(() => {
    console.log("then11");
    new Promise((resolve, reject) => {
      console.log("promise2");
      resolve();
    })
      .then(() => {
        console.log("then21");
      })
      .then(() => {
        console.log("then23");
      });
  })
  .then(() => {
    console.log("then12");
  });

const p2 = new Promise((resolve, reject) => {
  console.log("promise3");
  resolve();
}).then(() => {
  console.log("then31");
});

首先打印出 promise1

接著將 then11,promise2 添加到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["then11", "promise2"]

打印出 promise3,將 then31 添加到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["then11", "promise2", "then31"]

依次打印出 then11,promise2then31,此時(shí)微任務(wù)隊(duì)列為空

then21then12 添加到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["then21", "then12"]

依次打印出 then21,then12,此時(shí)微任務(wù)隊(duì)列為空

then23 添加到微任務(wù)隊(duì)列,此時(shí)微任務(wù)隊(duì)列為 ["then23"]

打印出 then23

第二道題

這道題實(shí)際在考察 Promise 的用法,當(dāng)在 then() 方法中返回一個(gè) Promise,p1 的第二個(gè)完成處理函數(shù)就會(huì)掛在返回的這個(gè) Promise 的 then() 方法下,因此輸出順序如下。

const p1 = new Promise((resolve, reject) => {
  console.log("promise1"); // 1
  resolve();
})
  .then(() => {
    console.log("then11"); // 2
    return new Promise((resolve, reject) => {
      console.log("promise2"); // 3
      resolve();
    })
      .then(() => {
        console.log("then21"); // 4
      })
      .then(() => {
        console.log("then23"); // 5
      });
  })
  .then(() => {
    console.log("then12"); //6
  });
最后

歡迎關(guān)注我的微信公眾號(hào):進(jìn)擊的前端

參考

《你不知道的 JavaScript (中卷)》—— Kyle Simpson

這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制

從一道題淺說 JavaScript 的事件循環(huán)

微任務(wù)、宏任務(wù)與 Event-Loop

前端基礎(chǔ)進(jìn)階:詳細(xì)圖解 JavaScript 內(nèi)存空間

詳解 JavaScript 中的 Event Loop(事件循環(huán))機(jī)制

Eventloop 不可怕,可怕的是遇上 Promise

圖解搞懂 JavaScript 引擎 Event Loop

JavaScript 線程機(jī)制與事件機(jī)制

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7399.html

相關(guān)文章

  • 一次搞懂CSS字體單位:px、em、rem和%

    摘要:網(wǎng)頁(yè)單位絕對(duì)單位,代表屏幕中每個(gè)點(diǎn)。相對(duì)單位,每個(gè)元素透過倍數(shù)乘以根元素的值。和就是固定百分比為單位,為父層的,為父層的。 對(duì)于繪圖和印刷而言,單位相當(dāng)重要,然而在網(wǎng)頁(yè)排版里,單位也是同樣具有重要性,在CSS3普及以來,更支持了一些方便好用的單位(px、em、rem…等),這篇文章將整理這些常用的CSS單位,也幫助自己未來在使用上能更加得心應(yīng)手。 網(wǎng)頁(yè)和印刷的單位若要把單位做區(qū)隔,最簡(jiǎn)...

    forsigner 評(píng)論0 收藏0
  • 徹底搞懂瀏覽器Event-loop

    摘要:檢查宏任務(wù)隊(duì)列,發(fā)現(xiàn)有的回調(diào)函數(shù)立即執(zhí)行回調(diào)函數(shù)輸出。接著遇到它的作用是在后將回調(diào)函數(shù)放到宏任務(wù)隊(duì)列中這個(gè)任務(wù)在再下一次的事件循環(huán)中執(zhí)行。 為什么會(huì)寫這篇博文呢? 前段時(shí)間,和頭條的小伙伴聊天問頭條面試前端會(huì)問哪些問題,他稱如果是他面試的話,event-loop肯定是要問的。那天聊了蠻多,event-loop算是給我留下了很深的印象,原因很簡(jiǎn)單,因?yàn)橹拔覐奈瓷钊肓私膺^,如果是面試的時(shí)...

    source 評(píng)論0 收藏0
  • Node.js Event Loop之Timers, process.nextTick()

    摘要:前言以異步和事件驅(qū)動(dòng)的特性著稱但異步是怎么實(shí)現(xiàn)的呢其中核心的一部分就是下文中內(nèi)容基本來自于文檔有不準(zhǔn)確地方請(qǐng)指出什么是能讓的操作表現(xiàn)得無阻塞盡管是單線程的但通過盡可能的將操作放到操作系統(tǒng)內(nèi)核由于現(xiàn)在大多數(shù)內(nèi)核都是多線程的它們可以在后臺(tái)執(zhí)行多 前言 Node.js以異步I/O和事件驅(qū)動(dòng)的特性著稱,但異步I/O是怎么實(shí)現(xiàn)的呢?其中核心的一部分就是event loop,下文中內(nèi)容基本來自于N...

    sarva 評(píng)論0 收藏0
  • 搞懂JavaScript引擎運(yùn)行原理

    摘要:同步一次執(zhí)行一件事,同步引擎一次只執(zhí)行一行,是同步的。調(diào)用函數(shù)將其推入堆棧并從函數(shù)返回將其彈出堆棧。執(zhí)行上下文當(dāng)函數(shù)放入到調(diào)用堆棧時(shí)由創(chuàng)建的環(huán)境。執(zhí)行結(jié)果它會(huì)立即被推到回調(diào)隊(duì)列,但它仍然會(huì)等待調(diào)用堆棧為空才會(huì)執(zhí)行。 為了保證可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 一些名詞 JS引擎 — 一個(gè)讀取代碼并運(yùn)行的引擎,沒有單一的J...

    lastSeries 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

gself

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<