主頁 > 企業開發 > [技術翻譯]在現代JavaScript中撰寫異步任務

[技術翻譯]在現代JavaScript中撰寫異步任務

2020-10-05 05:23:27 企業開發

本周再來翻譯一些技術文章,本次預計翻譯三篇文章如下:

  • 04.[譯]使用Nuxt生成靜態網站(Generate Static Websites with Nuxt)

  • 05.[譯]Web網頁內容是如何影響電池功耗的(How Web Content Can Affect Power Usage)

  • 06.[譯]在現代JavaScript中撰寫異步任務(https://web.dev/off-main-thread/)

我翻譯的技術文章都放在一個github倉庫中,如果覺得有用請點擊star收藏,我為什么要創建這個git倉庫?目的是通過翻譯國外的web相關的技術文章來學習和跟進web發展的新思想和新技術,git倉庫地址:https://github.com/yzsunlei/javascript-article-translate

在本文中,我們將探討過去圍繞異步執行的JavaScript的演變以及它如何改變我們撰寫和讀取代碼的方式,我們將從Web開發的開始,一直到現代異步模式示例,
JavaScript作為編程語言具有兩個主要特征,這兩個特征對于理解我們的代碼是如何作業的都很重要,首先是它的同步特性,這意味著代碼將幾乎在您閱讀時逐行運行,其次,它是單執行緒的,任何時候都只執行一個命令,

隨著語言的發展,新的模塊出現在場景中以允許異步執行,開發人員在解決更復雜的演算法和資料流時嘗試了不同的方法,從而導致圍繞它們的新介面和模式的出現,

同步執行和觀察者模式

如引言中所述,JavaScript通常會逐行運行您撰寫的代碼,即使在最初的幾年中,該語言也有例外,盡管它們很少,您可能已經知道它們:HTTP請求,DOM事件和時間間隔,

const button = document.querySelector('button');

// observe for user interaction
button.addEventListener('click', function(e) {
  console.log('user click just happened!');
})

如果添加事件偵聽器(例如,單擊元素并觸發用戶互動),則JavaScript引擎會將事件偵聽器回呼的任務放入佇列,但將繼續執行其當前堆疊中的內容,完成那里的呼叫之后,它現在將運行偵聽器的回呼,

此行為類似于網路請求和計時器發生的情況,它們是Web開發人員訪問異步執行的第一個模塊,

盡管這些是JavaScript中常見的同步執行例外的,但至關重要的是要了解該語言仍然是單執行緒的,并且盡管它可以將Task排隊,異步運行它們然后回傳主執行緒,但它只能一次執行一段代碼,

我們的工具手冊,其中Alla Kholmatova探索了如何創建有效且可維護的設計系統來設計出色的數字產品,認識Design Systems,了解常見的陷阱,陷阱和Alla多年來汲取的經驗教訓,

例如,讓我們發送一個網路請求,

var request = new XMLHttpRequest();
request.open('GET', '//some.api.at/server', true);

// observe for server response
request.onreadystatechange = function() {
  if (request.readyState === 4 && xhr.status === 200) {
    console.log(request.responseText);
  }
}

request.send();

服務器回傳時,分配給該方法的任務將onreadystatechange放入佇列(代碼在主執行緒中繼續執行),

注意:解釋JavaScript引擎如何將任務排隊和處理執行執行緒是一個很復雜的主題,可能值得一讀,不過,我還是建議您查看“事件回圈到底是什么?”菲利普·羅伯茨(Phillip Roberts)提供的幫助,以幫助您更好地理解,

在上述每種情況下,我們都在回應外部事件,達到一定的時間間隔,用戶操作或服務器回應,我們本身無法創建異步任務,我們始終觀察到發生的事件超出了我們的范圍,

這就是為什么將這種模板式的代碼稱為“觀察者模式”,addEventListener在這種情況下,可以更好地由介面表示,很快,暴露這種模式的事件庫或框架蓬勃發展,

NODE.JS和事件觸發器

一個很好的例子是Node.js,該頁面將自己描述為“異步事件驅動的JavaScript運行時”,因此事件觸發器和回呼是一等公民,它甚至用EventEmitter已經實作了一個建構式,

const EventEmitter = require('events');
const emitter = new EventEmitter();

// respond to events
emitter.on('greeting', (message) => console.log(message));

// send events
emitter.emit('greeting', 'Hi there!');

這不僅是異步執行的通用方法,而且是其生態系統的核心模式和慣例,Node.js開辟了一個在不同環境中甚至在網路之外撰寫JavaScript的新時代,結果,其他異步情況也是可能的,例如創建新目錄或寫入檔案,

const { mkdir, writeFile } = require('fs');

const styles = 'body { background: #ffdead; }';

mkdir('./assets/', (error) => {
  if (!error) {
    writeFile('assets/main.css', styles, 'utf-8', (error) => {
      if (!error) console.log('stylesheet created');
    })
  }
})

您可能會注意到,回呼error函式的第一個引數為,如果需要回應資料,則將其作為第二個引數,這被稱為“錯誤優先回呼模式”,它成為作者和貢獻者為其自己的程式包和庫所采用的約定,

Promise和無盡的回呼鏈

隨著Web開發面臨更復雜的問題需要解決,對更好的異步工件的需求出現了,如果我們查看最后一個代碼片段,我們會看到重復的回呼鏈,隨著任務數量的增加,回呼鏈的擴展效果就很差,

例如,讓我們僅添加兩個步驟,即檔案讀取和樣式預處理,

const { mkdir, writeFile, readFile } = require('fs');
const less = require('less')

readFile('./main.less', 'utf-8', (error, data) => {
  if (error) throw error
  less.render(data, (lessError, output) => {
    if (lessError) throw lessError
    mkdir('./assets/', (dirError) => {
      if (dirError) throw dirError
      writeFile('assets/main.css', output.css, 'utf-8', (writeError) => {
        if (writeError) throw writeError
        console.log('stylesheet created');
      })
    })
  })
})

我們可以看到,由于多個回呼鏈和重復的錯誤處理,隨著正在撰寫的程式變得越來越復雜,代碼變得更加難以為人所知,

Promise,包裝和連鎖模式

Promises最初宣布它們是JavaScript語言的新功能時,并沒有引起太多關注,它們并不是一個新概念,因為其他語言在幾十年前就已經實作了類似的功能,事實是,自從出現以來,他們發現我所做的大多數專案的語意和結構都發生了很大變化,

Promises不僅引入了供開發人員撰寫異步代碼的內置解決方案,而且還為Web開發(如Web規范)的新功能的構建基礎打開了Web開發的新階段fetch,

從回呼方法遷移到基于Promise的方法在專案(例如庫和瀏覽器)中變得越來越普遍,甚至Node.js也開始緩慢地遷移到它們,

例如,包裝一下Node的readFile方法:

const { readFile } = require('fs');

const asyncReadFile = (path, options) => {
  return new Promise((resolve, reject) => {
    readFile(path, options, (error, data) => {
      if (error) reject(error);
      else resolve(data);
    })
  });
}

在這里,我們通過在Promise建構式中執行,resolve在方法結果成功時以及reject在定義錯誤物件時呼叫,來掩蓋回呼,

當一個方法回傳一個Promise物件時,我們可以通過將一個函式傳遞給來遵循其成功的決議then,其引數是Promise被決議的值,在這種情況下為data,

如果在方法期間引發錯誤catch,則將呼叫該函式(如果存在),

注意:如果您需要更深入地了解Promises的作業方式,我建議Jake Archibald 在Google的Web開發博客上寫的“JavaScript Promises:Introduction”一文,

現在我們可以使用這些新方法并避免回呼鏈,

asyncRead('./main.less', 'utf-8')
  .then(data =https://www.cnblogs.com/yzsunlei/p/> console.log('file content', data))
  .catch(error => console.error('something went wrong', error))

具有創建異步任務的方法和清晰的界面以跟蹤其可能的結果,使該行業擺脫了觀察者模式,基于Promise的代碼似乎可以解決不可讀且容易出錯的代碼,

隨著更好的語法或更清晰的錯誤訊息在編碼時突出顯示有所幫助,對于開發人員來說,更易于推理的代碼變得更具可預測性,并且執行路徑的情況更好,更容易捕捉可能的代碼陷阱,

Promises由于在社區中的普及程度很高,Node.js迅速發布了其I/O方法的內置版本以回傳Promise物件,例如從中匯入檔案操作fs.promises,

它甚至提供了一個promisify實用工具,用于包裝遵循錯誤優先回呼模式的所有函式,并將其轉換為基于Promise的函式,

但是,Promises在所有情況下都能提供幫助嗎?

讓我們重新想象一下用Promises撰寫的樣式預處理任務,

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

readFile('./main.less', 'utf-8')
  .then(less.render)
  .then(result =>
    mkdir('./assets')
      .then(writeFile('assets/main.css', result.css, 'utf-8'))
  )
  .catch(error => console.error(error))

代碼中的冗余明顯減少了,尤其是在我們現在所依賴的錯誤處理方面catch,但是Promises某種程度上未能提供與操作串聯直接相關的清晰代碼縮進,

這實際上是在呼叫then之后的第一個陳述句上實作的readFile,這些行之后發生的事情是需要創建一個新的作用域,我們可以在該作用域中首先創建目錄,然后將結果寫入檔案中,這就導致了縮進節奏的中斷,乍看之下很難確定指令序列,

解決此問題的一種方法是預先處理該問題的自定義方法,并允許該方法正確連接,但是我們將向似乎已經具有實作任務所需功能的代碼引入更多的復雜性,

注意:這是一個示例程式,我們可以控制某些方法,它們都遵循行業慣例,但并非總是如此,通過更復雜的串聯或引入具有不同型別的庫,我們可以輕松破壞代碼風格,

令人高興的是,JavaScript社區再次從其他語言語法中學到了東西,并添加了一種表示法,可以在很多情況下幫助異步任務串聯而不是像同步代碼那樣令人愉悅或直截了當,

async和await

A Promise在執行時被定義為一個未決議的值,創建a的實體Promise是對該模塊的顯式呼叫,

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

readFile('./main.less', 'utf-8')
  .then(less.render)
  .then(result =>
    mkdir('./assets')
      .then(writeFile('assets/main.css', result.css, 'utf-8'))
  )
  .catch(error => console.error(error))

在async方法內部,我們可以使用await保留字來確定a的解析度,Promise然后繼續執行,

讓我們使用此語法重新訪問或撰寫代碼段,

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

async function processLess() {
  const content = await readFile('./main.less', 'utf-8')
  const result = await less.render(content)
  await mkdir('./assets')
  await writeFile('assets/main.css', result.css, 'utf-8')
}

processLess()

注意:請注意,由于我們今天不能在異步函式的范圍之外使用,因此需要將所有代碼移至方法await,

每次async方法找到一條await陳述句時,它將停止執行,直到處理中的值或Promise被決議為止,

盡管異步執行,但使用async/await表示法會有明顯的后果,代碼看起來好像是async,這是我們開發人員更習慣查看和推理的,

錯誤處理呢?為此,我們使用在該語言中已經存在很長時間的陳述句,try和catch,

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less');

async function processLess() {
  try {
    const content = await readFile('./main.less', 'utf-8')
    const result = await less.render(content)
    await mkdir('./assets')
    await writeFile('assets/main.css', result.css, 'utf-8')
  } catch(e) {
    console.error(e)
  }
}

processLess()

放心,在該程序中引發的任何錯誤將由該catch陳述句內的代碼處理,我們在中心位置負責錯誤處理,但是現在我們有了一個易于閱讀和遵循的代碼,

具有回傳值的后續操作不需要存盤在mkdir不會破壞代碼節奏的變數中;也無需在以后的步驟中創建新的作用域來訪問result的值,

可以肯定地說,Promises是該語言中引入的一個基本模塊,對于在JavaScript中啟用async/await表示法是必需的,您可以在現代瀏覽器和最新版本的Node.js中使用它,

注意:最近在JSConf中,Node的創建者和第一貢獻者Ryan Dahl很遺憾沒有堅持Promises的早期開發,主要是因為Node的目標是創建事件驅動的服務器和檔案管理,而Observer模式更適合于此,

結論

將Promises引入Web開發世界的目的是改變我們在代碼中排隊操作的方式,并改變了我們對代碼執行進行推理的方式以及我們撰寫庫和包的方式,

但是擺脫回呼鏈很難解決,我認為then在多年習慣于觀察者模式和主要提供商采用的方法之后,不得不通過一種方法并不能幫助我們擺脫思路,像Node.js這樣的社區,

正如諾蘭·勞森(Nolan Lawson)在其有關Promise串聯中錯誤使用的出色文章中所說,舊的回呼習慣會死掉!稍后,他解釋了如何避免這些陷阱,

我認為Promises是中間步驟,它允許以自然的方式生成異步任務,但并沒有幫助我們進一步改進更好的代碼模式,有時您實際上需要更適應和改進的語言語法,

當我們嘗試使用JavaScript解決更復雜的難題時,我們看到了對更成熟語言的需求,并嘗試了以前不曾在網路上看到過的架構和模式,

我們仍然不知道ECMAScript規范的表現如何,因為我們一直將JavaScript治理擴展到網路之外,并嘗試解決更復雜的難題,

現在很難說我們需要從語言中真正地將這些難題轉變成更簡單的程式所需要的東西,但是我對Web和JavaScript本身如何推動事物,試圖適應挑戰和新環境感到滿意,與十年前開始在瀏覽器中撰寫代碼相比,現在我覺得JavaScript是一個更加異步的友好的地方,

原文鏈接:https://www.smashingmagazine.com/2019/10/asynchronous-tasks-modern-javascript/

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/156532.html

標籤:JavaScript

上一篇:獲取滾動的頭部距離和左邊距離

下一篇:vue實作網路圖片瀑布流 + 下拉重繪 + 上拉加載更多

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more