摘要:下面我將介紹的基本用法以及如何在異步編程中使用它們。在沒有發(fā)布之前,作為異步編程主力軍的回調(diào)函數(shù)一直被人詬病,其原因有太多比如回調(diào)地獄代碼執(zhí)行順序難以追蹤后期因代碼變得十分復(fù)雜導(dǎo)致無法維護(hù)和更新等,而的出現(xiàn)在很大程度上改變了之前的窘境。
前言
自己著手準(zhǔn)備寫這篇文章的初衷是覺得如果想要更深入的理解 JS,異步編程則是必須要跨過的一道坎。由于這里面涉及到的東西很多也很廣,在初學(xué) JS 的時(shí)候可能無法完整的理解這一概念,即使在現(xiàn)在來看還是有很多自己沒有接觸和理解到的知識點(diǎn),但是為了跨過這道坎,我仍然愿意鼓起勇氣用我已經(jīng)掌握的部分知識盡全力講述一下 JS 中的異步編程。如果我所講的一些概念或術(shù)語有錯(cuò)誤,請讀者向我指出問題所在,我會立即糾正更改。
同步與異步我們知道無論是在瀏覽器端還是在服務(wù)器 ( Node ) 端,JS 的執(zhí)行都是在單線程下進(jìn)行的。我們以瀏覽器中的 JS 執(zhí)行線程為例,在這個(gè)線程中 JS 引擎會創(chuàng)建執(zhí)行上下文棧,之后我們的代碼就會作為執(zhí)行上下文 ( 全局、函數(shù)、eval ) 像一系列任務(wù)一樣在執(zhí)行上下文棧中按照后進(jìn)先出 ( LIFO ) 的方式依次執(zhí)行。而同步最大的特性就是會阻塞后面任務(wù)的執(zhí)行,比如此時(shí) JS 正在執(zhí)行大量的計(jì)算,這個(gè)時(shí)候就會使線程阻塞從而導(dǎo)致頁面渲染加載不連貫 ( 在瀏覽器端的 Event Loop 中每次執(zhí)行棧中的任務(wù)執(zhí)行完畢后都會去檢查并執(zhí)行事件隊(duì)列里面的任務(wù)直到隊(duì)列中的任務(wù)為空,而事件隊(duì)列中的任務(wù)又分為微隊(duì)列與宏隊(duì)列,當(dāng)微隊(duì)列中的任務(wù)執(zhí)行完后才會去執(zhí)行宏隊(duì)列中的任務(wù),而在微隊(duì)列任務(wù)執(zhí)行完到宏隊(duì)列任務(wù)開始之前瀏覽器的 GUI 線程會執(zhí)行一次頁面渲染 ( UI rendering ),這也就解釋了為什么在執(zhí)行棧中進(jìn)行大量的計(jì)算時(shí)會阻塞頁面的渲染 ) 。
與同步相對的異步則可以理解為在異步操作完成后所要做的任務(wù),它們通常以回調(diào)函數(shù)或者 Promise 的形式被放入事件隊(duì)列,再由事件循環(huán) ( Event Loop ) 機(jī)制在每次輪詢時(shí)檢查異步操作是否完成,若完成則按事件隊(duì)列里面的執(zhí)行規(guī)則來依次執(zhí)行相應(yīng)的任務(wù)。也正是得益于事件循環(huán)機(jī)制的存在,才使得異步任務(wù)不會像同步任務(wù)那樣完全阻塞 JS 執(zhí)行線程。
異步操作一般包括 網(wǎng)絡(luò)請求 、文件讀取 、數(shù)據(jù)庫處理
異步任務(wù)一般包括 setTimout / setInterval 、Promise 、requestAnimationFrame ( 瀏覽器獨(dú)有 ) 、setImmediate ( Node 獨(dú)有 ) 、process.nextTick ( Node 獨(dú)有 ) 、etc ...
注意: 在瀏覽器端與在 Node 端的 Event Loop 機(jī)制是有所不同的,下面給出的兩張圖簡要闡述了在不同環(huán)境下事件循環(huán)的運(yùn)行機(jī)制,由于 Event Loop 不是本文內(nèi)容的重點(diǎn),但是 JS 異步編程又是建立在它的基礎(chǔ)之上的,故在下面給出相應(yīng)的閱讀鏈接,希望能夠幫助到有需要的讀者。
瀏覽器端
Node 端
閱讀鏈接
深入理解 JS 事件循環(huán)機(jī)制 ( 瀏覽器篇 )
深入理解 JS 事件循環(huán)機(jī)制 ( Node.js 篇 )
為異步而生的 JS 語法回望歷史,在最近幾年里 ECMAScript 標(biāo)準(zhǔn)幾乎每年都有版本的更新,也正是因?yàn)橛邢?ES6 這種在語言特性上大版本的更新,到了現(xiàn)今的 8102 年, JS 中的異步編程相對于那個(gè)只有回調(diào)函數(shù)的遠(yuǎn)古時(shí)代有了很大的進(jìn)步。下面我將介紹 callback 、Promise 、generator 、async / await 的基本用法以及如何在異步編程中使用它們。
callback回調(diào)函數(shù)并不算是 JS 中的語法但它卻是解決異步編程問題中最常用的一種方法,所以在這里有必要提出來,下面舉一個(gè)例子,大家看一眼就懂。
const foo = function (x, y, cb) { setTimeout(() => { cb(x + y) }, 2000) } // 使用 thunk 函數(shù),有點(diǎn)函數(shù)柯里化的味道,在最后處理 callback。 const thunkify = function (fn) { return function () { let args = Array.from(arguments) return function (cb) { fn.apply(null, [...args, cb]) } } } let fooThunkory = thunkify(foo) let fooThunk1 = fooThunkory(2, 8) let fooThunk2 = fooThunkory(4, 16) fooThunk1((sum) => { console.log(sum) // 10 }) fooThunk2((sum) => { console.log(sum) // 20 })Promise
在 ES6 沒有發(fā)布之前,作為異步編程主力軍的回調(diào)函數(shù)一直被人詬病,其原因有太多比如回調(diào)地獄、代碼執(zhí)行順序難以追蹤、后期因代碼變得十分復(fù)雜導(dǎo)致無法維護(hù)和更新等,而 Promise 的出現(xiàn)在很大程度上改變了之前的窘境。話不多說先直接上代碼提前感受下它的魅力,然后我再總結(jié)下自己認(rèn)為在 Promise 中很重要的幾個(gè)點(diǎn)。
const foo = function () { let args = [...arguments] let cb = args.pop() setTimeout(() => { cb(...args) }, 2000) } const promisify = function (fn) { return function () { let args = [...arguments] return function (cb) { return new Promise((resolve, reject) => { fn.apply(null, [...args, resolve, reject, cb]) }) } } } const callback = function (x, y, isAdd, resolve, reject) { if (isAdd) { resolve(x + y) } else { reject("Add is not allowed.") } } let promisory = promisify(foo) let p1 = promisory(4, 16, false) let p2 = promisory(2, 8, true) p1(callback) .then((sum) => { console.log(sum) }, (err) => { console.error(err) // Add is not allowed. }) .finally(() => { console.log("Triggered once the promise is settled.") }) p2(callback) .then((sum) => { console.log(sum) // 10 return "evil
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98528.html
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:接下來,我們一起來看看中的異步編程,具體有哪幾種。實(shí)現(xiàn)異步編程的方法一回調(diào)函數(shù)上面不止一次提到了回調(diào)函數(shù)。它是異步編程中,最基本的方法。四對象接下來,我們聊聊與相關(guān)的異步編程方法,對象。 showImg(https://segmentfault.com/img/bVbneWy?w=1600&h=1200); 前言 最近,小伙伴S 問了我一段代碼: const funB = (value...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:回調(diào)函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因?yàn)槭录]有被觸發(fā)或者條件不滿足。同步方式請求異步同步請求當(dāng)請求開始發(fā)送時(shí),瀏覽器事件線程通知主線程,讓線程發(fā)送數(shù)據(jù)請求,主線程收到 一直以來都知道JavaScript是一門單線程語言,在筆試過程中不斷的遇到一些輸出結(jié)果的問題,考量的是對異步編程掌握情況。一般被問到異步的時(shí)候腦子里第一反應(yīng)就是Ajax,setTimse...
摘要:異步編程解決方案筆記最近讀了樸靈老師的深入淺出中異步編程一章,并參考了一些有趣的文章。另外回調(diào)函數(shù)中的也失去了意義,這會使我們的程序必須依賴于副作用。 JavaScript 異步編程解決方案筆記 最近讀了樸靈老師的《深入淺出NodeJS》中《異步編程》一章,并參考了一些有趣的文章。在此做個(gè)筆記,記錄并鞏固學(xué)到的知識。 JavaScript異步編程的兩個(gè)核心難點(diǎn) 異步I/O、事件驅(qū)動使得...
摘要:調(diào)用棧被清空,消息隊(duì)列中并無任務(wù),線程停止,事件循環(huán)結(jié)束。不確定的時(shí)間點(diǎn)請求返回,將設(shè)定好的回調(diào)函數(shù)放入消息隊(duì)列。調(diào)用棧執(zhí)行完畢執(zhí)行消息隊(duì)列任務(wù)。請求并發(fā)回調(diào)函數(shù)執(zhí)行順序無法確定。 異步編程 JavaScript中異步編程問題可以說是基礎(chǔ)中的重點(diǎn),也是比較難理解的地方。首先要弄懂的是什么叫異步? 我們的代碼在執(zhí)行的時(shí)候是從上到下按順序執(zhí)行,一段代碼執(zhí)行了之后才會執(zhí)行下一段代碼,這種方式...
閱讀 3349·2021-09-30 09:47
閱讀 2732·2021-08-18 10:22
閱讀 2518·2021-08-16 10:49
閱讀 2884·2019-08-30 15:53
閱讀 2732·2019-08-29 16:14
閱讀 3185·2019-08-28 18:18
閱讀 3229·2019-08-26 13:21
閱讀 787·2019-08-26 12:02