JS--異步的日常用法

目錄

  • JS 異步編程
    • 并發(concurrency)和并行(parallelism)區別
    • 回調函數(Callback)
    • Generator
    • Promise
    • async 及 await
    • 常用定時器函數

JS 異步編程

并發(concurrency)和并行(parallelism)區別

涉及面試題:并發與并行的區別?

這兩個名詞確實是很多人都常會混淆的知識點。其實混淆的原因可能只是兩個名詞在中文上的相似,在英文上來說完全是不同的單詞。

并發是宏觀概念,我分別有任務 A 和任務 B,在一段時間內通過任務間的切換完成了這兩個任務,這種情況就可以稱之為并發。

并行是微觀概念,假設 CPU 中存在兩個核心,那么我就可以同時完成任務 A、B。同時完成多個任務的情況就可以稱之為并行。

回調函數(Callback)

涉及面試題:什么是回調函數?回調函數有什么缺點?如何解決回調地獄問題?

回調函數應該是大家經常使用到的,以下代碼就是一個回調函數的例子:

ajax(url, () => {// 處理邏輯
})

但是回調函數有一個致命的弱點,就是容易寫出回調地獄(Callback hell)。假設多個請求存在依賴性,你可能就會寫出如下代碼:

ajax(url, () => {// 處理邏輯ajax(url1, () => {// 處理邏輯ajax(url2, () => {// 處理邏輯})})
})

以上代碼看起來不利于閱讀和維護,當然,你可能會想說解決這個問題還不簡單,把函數分開來寫不就得了

function firstAjax() {ajax(url1, () => {// 處理邏輯secondAjax()})
}
function secondAjax() {ajax(url2, () => {// 處理邏輯})
}
ajax(url, () => {// 處理邏輯firstAjax()
})

以上的代碼雖然看上去利于閱讀了,但是還是沒有解決根本問題。

回調地獄的根本問題就是:

  1. 嵌套函數存在耦合性,一旦有所改動,就會牽一發而動全身
  2. 嵌套函數一多,就很難處理錯誤

當然,回調函數還存在著別的幾個缺點,比如不能使用 try catch 捕獲錯誤,不能直接 return。在接下來的幾小節中,我們將來學習通過別的技術解決這些問題。

Generator

涉及面試題:你理解的 Generator 是什么?

Generator 算是 ES6 中難理解的概念之一了,Generator 最大的特點就是可以控制函數的執行。在這一小節中我們不會去講什么是 Generator,而是把重點放在 Generator 的一些容易困惑的地方。

function *foo(x) {let y = 2 * (yield (x + 1))let z = yield (y / 3)return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}

你也許會疑惑為什么會產生與你預想不同的值,接下來就讓我為你逐行代碼分析原因

  • 首先 Generator 函數調用和普通函數不同,它會返回一個迭代器
  • 當執行第一次 next 時,傳參會被忽略,并且函數暫停在 yield (x + 1) 處,所以返回 5 + 1 = 6
  • 當執行第二次 next 時,傳入的參數等于上一個 yield 的返回值,如果你不傳參,yield 永遠返回 undefined。此時 let y = 2 * 12,所以第二個 yield 等于 2 * 12 / 3 = 8
  • 當執行第三次 next 時,傳入的參數會傳遞給 z,所以 z = 13, x = 5, y = 24,相加等于 42

Generator 函數一般見到的不多,其實也于他有點繞有關系,并且一般會配合 co 庫去使用。當然,我們可以通過 Generator 函數解決回調地獄的問題,可以把之前的回調地獄例子改寫為如下代碼:

function *fetch() {yield ajax(url, () => {})yield ajax(url1, () => {})yield ajax(url2, () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

Promise

涉及面試題:Promise 的特點是什么,分別有什么優缺點?什么是 Promise 鏈?Promise 構造函數執行和 then 函數執行有什么區別?

Promise 翻譯過來就是承諾的意思,這個承諾會在未來有一個確切的答復,并且該承諾有三種狀態,分別是:

  1. 等待中(pending)
  2. 完成了 (resolved)
  3. 拒絕了(rejected)

這個承諾一旦從等待狀態變成為其他狀態就永遠不能更改狀態了,也就是說一旦狀態變為 resolved 后,就不能再次改變

new Promise((resolve, reject) => {resolve('success')// 無效reject('reject')
})

當我們在構造 Promise 的時候,構造函數內部的代碼是立即執行的

new Promise((resolve, reject) => {console.log('new Promise')resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh

Promise 實現了鏈式調用,也就是說每次調用 then 之后返回的都是一個 Promise,并且是一個全新的 Promise,原因也是因為狀態不可變。如果你在 then 中 使用了 return,那么 return 的值會被 Promise.resolve() 包裝

Promise.resolve(1).then(res => {console.log(res) // => 1return 2 // 包裝成 Promise.resolve(2)}).then(res => {console.log(res) // => 2})

當然了,Promise 也很好地解決了回調地獄的問題,可以把之前的回調地獄例子改寫為如下代碼:

ajax(url).then(res => {console.log(res)return ajax(url1)}).then(res => {console.log(res)return ajax(url2)}).then(res => console.log(res))

前面都是在講述 Promise 的一些優點和特點,其實它也是存在一些缺點的,比如無法取消 Promise,錯誤需要通過回調函數捕獲。

async 及 await

涉及面試題:async 及 await 的特點,它們的優點和缺點分別是什么?await 原理是什么?

一個函數如果加上 async ,那么該函數就會返回一個 Promise

async function test() {return "1"
}
console.log(test()) // -> Promise {<resolved>: "1"}

async 就是將函數返回值使用 Promise.resolve() 包裹了下,和 then 中處理返回值一樣,并且 await 只能配套 async 使用

async function test() {let value = await sleep()
}

asyncawait 可以說是異步終極解決方案了,相比直接使用 Promise 來說,優勢在于處理 then 的調用鏈,能夠更清晰準確的寫出代碼,畢竟寫一大堆 then 也很惡心,并且也能優雅地解決回調地獄問題。當然也存在一些缺點,因為 await 將異步代碼改造成了同步代碼,如果多個異步代碼沒有依賴性卻使用了 await 會導致性能上的降低。

async function test() {// 以下代碼沒有依賴性的話,完全可以使用 Promise.all 的方式// 如果有依賴性的話,其實就是解決回調地獄的例子了await fetch(url)await fetch(url1)await fetch(url2)
}

下面來看一個使用 await 的例子:

let a = 0
let b = async () => {a = a + await 10console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1

對于以上代碼你可能會有疑惑,讓我來解釋下原因

  • 首先函數 b 先執行,在執行到 await 10 之前變量 a 還是 0,因為 await 內部實現了 generatorgenerator 會保留堆棧中東西,所以這時候 a = 0 被保存了下來
  • 因為 await 是異步操作,后來的表達式不返回 Promise 的話,就會包裝成 Promise.reslove(返回值),然后會去執行函數外的同步代碼
  • 同步代碼執行完畢后開始執行異步代碼,將保存下來的值拿出來使用,這時候 a = 0 + 10

上述解釋中提到了 await 內部實現了 generator,其實 await 就是 generator 加上 Promise 的語法糖,且內部實現了自動執行 generator。如果你熟悉 co 的話,其實自己就可以實現這樣的語法糖。

常用定時器函數

涉及面試題:setTimeout、setInterval、requestAnimationFrame 各有什么特點?

異步編程當然少不了定時器了,常見的定時器函數有 setTimeoutsetIntervalrequestAnimationFrame。我們先來講講最常用的setTimeout,很多人認為 setTimeout 是延時多久,那就應該是多久后執行。

其實這個觀點是錯誤的,因為 JS 是單線程執行的,如果前面的代碼影響了性能,就會導致 setTimeout 不會按期執行。當然了,我們可以通過代碼去修正 setTimeout,從而使定時器相對準確

let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = intervalfunction loop() {count++// 代碼執行所消耗的時間let offset = new Date().getTime() - (startTime + count * interval);let diff = end - new Date().getTime()let h = Math.floor(diff / (60 * 1000 * 60))let hdiff = diff % (60 * 1000 * 60)let m = Math.floor(hdiff / (60 * 1000))let mdiff = hdiff % (60 * 1000)let s = mdiff / (1000)let sCeil = Math.ceil(s)let sFloor = Math.floor(s)// 得到下一次循環所消耗的時間currentInterval = interval - offset console.log('時:'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代碼執行時間:'+offset, '下次循環間隔'+currentInterval) setTimeout(loop, currentInterval)
}setTimeout(loop, currentInterval)

接下來我們來看 setInterval,其實這個函數作用和 setTimeout 基本一致,只是該函數是每隔一段時間執行一次回調函數。

通常來說不建議使用 setInterval。第一,它和 setTimeout 一樣,不能保證在預期的時間執行任務。第二,它存在執行累積的問題,請看以下偽代碼

function demo() {setInterval(function(){console.log(2)},1000)sleep(2000)
}
demo()

以上代碼在瀏覽器環境中,如果定時器執行過程中出現了耗時操作,多個回調函數會在耗時操作結束以后同時執行,這樣可能就會帶來性能上的問題。

如果你有循環定時器的需求,其實完全可以通過 requestAnimationFrame 來實現

function setInterval(callback, interval) {let timerconst now = Date.nowlet startTime = now()let endTime = startTimeconst loop = () => {timer = window.requestAnimationFrame(loop)endTime = now()if (endTime - startTime >= interval) {startTime = endTime = now()callback(timer)}}timer = window.requestAnimationFrame(loop)return timer
}let a = 0
setInterval(timer => {console.log(1)a++if (a === 3) cancelAnimationFrame(timer)
}, 1000)

首先 requestAnimationFrame 自帶函數節流功能,基本可以保證在 16.6 毫秒內只執行一次(不掉幀的情況下),并且該函數的延時效果是精確的,沒有其他定時器時間不準的問題,當然你也可以通過該函數來實現 setTimeout

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207689.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207689.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207689.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python中一些有趣的例題

下面會寫一些基礎的例題&#xff0c;有興趣的自己也可以練練手&#xff01; 1.假設手機短信收到的數字驗證碼為“278902”&#xff0c;編寫一個程序&#xff0c;讓用戶輸入數字驗證碼&#xff0c;如果數字驗證碼輸入正確&#xff0c;提示“支付成功”&#xff1b;否則提示“數…

Python configparser 模塊:優雅處理配置文件的得力工具

更多資料獲取 &#x1f4da; 個人網站&#xff1a;ipengtao.com 配置文件在軟件開發中扮演著重要的角色&#xff0c;而Python中的 configparser 模塊提供了一種優雅而靈活的方式來處理各種配置需求。本文將深入介紹 configparser 模塊的各個方面&#xff0c;通過豐富的示例代碼…

嵌入式雜記 - MDK的Code, RO-data , RW-data, ZI-data意思

嵌入式雜記 - Keil的Code, RO-data , RW-data, ZI-data意思 MDK中的數據分類MCU中的內部存儲分布MDK中數據類型存儲Code代碼段例子 RO-data 只讀數據段例子 RW-data 可讀寫數據段例子 ZI-data 清零數據段例子 在嵌入式開發中&#xff0c;我們經常都會使用一些IDE&#xff0c;例…

Hadoop學習筆記(HDP)-Part.17 安裝Spark2

目錄 Part.01 關于HDP Part.02 核心組件原理 Part.03 資源規劃 Part.04 基礎環境配置 Part.05 Yum源配置 Part.06 安裝OracleJDK Part.07 安裝MySQL Part.08 部署Ambari集群 Part.09 安裝OpenLDAP Part.10 創建集群 Part.11 安裝Kerberos Part.12 安裝HDFS Part.13 安裝Ranger …

Web前端 ---- 【Vue】Vuex的使用(輔助函數、模塊化開發)

目錄 前言 Vuex是什么 Vuex的配置 安裝vuex 配置vuex文件 Vuex核心對象 actions mutations getters state Vuex在vue中的使用 輔助函數 Vuex模塊化開發 前言 本文介紹一種新的用于組件傳值的插件 —— vuex Vuex是什么 Vuex 是一個專為 Vue.js 應用程序開發的狀態…

淺談前端代碼里的命名規范與注釋

淺談前端代碼里的命名規范與注釋 在前端代碼中&#xff0c;命名規范和注釋是非常重要的。它們不僅有助于提高代碼的可讀性和可維護性&#xff0c;還可以幫助開發者之間更好地協作和溝通。下面是一些關于命名規范和注釋的常見建議&#xff1a; 命名規范&#xff1a; 使用有意義…

【ArcGIS Pro微課1000例】0053:基于SQL Server創建與啟用地理數據庫

之前的文章有講述基于SQL Server創建企業級地理數據庫,本文講述在SQL Server中創建常規的關心數據庫,然后在ArcGIS Pro中將其啟用,轉換為企業級地理數據庫。 1. 在SQL Server中創建數據庫** 打開SQL Server 2019,連接到數據庫服務器。 展開數據庫連接,在數據庫上右鍵→新…

python中的lambda關鍵字

對于一切很模糊的知識&#xff0c;首要的是抓住概念的定義。 lambda&#xff1a;在 Python 中用于創建匿名函數的關鍵字。 也即&#xff0c;lambda是一種關鍵字&#xff0c;這種關鍵字的作用是創建匿名函數。 這一段很好懂&#xff0c;就是匿名函數有點懵。 什么是匿名函數&…

mybatis-plus構造器查詢

文章目錄 Hutool工具包Vo與entity轉換多表分頁查詢構造器&#xff1a;查詢構造器&#xff1a;拼接構造器&#xff1a;刪除操作構造器&#xff1a;修改操作查詢關鍵字 Hutool工具包Vo與entity轉換 BeanUtils&#xff1a;copyProperties(vo, entity)&#xff0c;vo轉實體類。 L…

在裝有 PostgreSQL 14 的 Red Hat8上安裝 `pg_cron`

要在裝有 PostgreSQL 14 的 Red Hat、CentOS、Fedora 或 Amazon Linux 上安裝 pg_cron&#xff0c;請遵循以下步驟。這些步驟假定您已經安裝了 PostgreSQL Global Development Group (PGDG) 的 PostgreSQL 版本。 安裝 pg_cron 擴展 使用 yum 安裝 pg_cron 擴展&#xff1a;s…

(四)Tiki-taka算法(TTA)求解無人機三維路徑規劃研究(MATLAB)

一、無人機模型簡介&#xff1a; 單個無人機三維路徑規劃問題及其建模_IT猿手的博客-CSDN博客 參考文獻&#xff1a; [1]胡觀凱,鐘建華,李永正,黎萬洪.基于IPSO-GA算法的無人機三維路徑規劃[J].現代電子技術,2023,46(07):115-120 二、Tiki-taka算法&#xff08;TTA&#xf…

基于SSH的java記賬管理系統

基于SSH的java記賬管理系統 一、系統介紹二、功能展示四、其他系統實現五、獲取源碼 一、系統介紹 項目類型&#xff1a;Java EE項目 項目名稱&#xff1a;基于SSH的記賬管理系統 項目架構&#xff1a;B/S架構 開發語言&#xff1a;Java語言 前端技術&#xff1a;HTML、CS…

初識優先級隊列與堆

1.優先級隊列 由前文隊列queue可知&#xff0c;隊列是一種先進先出(FIFO)的數據結構&#xff0c;但有些情況下&#xff0c;操作的數據可能帶有優先級&#xff0c;一般出隊列時&#xff0c;可能需要優先級高的元素先出隊列&#xff0c;在此情況下&#xff0c;使用隊列queue顯然不…

git常用命令指南

目錄 一、基本命令 1、創建分支 2、切換分支 3、合并分支 4、初始化空git倉庫 二、文件操作 1、創建文件 2、添加多個文件 3、查看項目的當前狀態 4、修改文件 5、刪除文件 6、提交項目 三、實際操作 1、創建目錄 2、進入新目錄 3、初始化空git倉庫 4、創建文…

C++STL的string模擬實現

文章目錄 前言string的成員變量成員函數構造函數拷貝構造賦值重載 模擬實現string各種接口print迭代器普通迭代器const迭代器 string比較大小push_backinsert 和 eraseinserterase reserve和resizereserveresize swapfindcout和cincoutcin 前言 今天要講string的底層實現&…

總線(什么是南北橋?您都用過哪些總線?)

什么是總線&#xff1f; 計算機系統中的總線&#xff08;Bus&#xff09;是指計算機設備和設備之間傳輸信息的公共數據通道&#xff0c;是連接計算機硬件系統內多種設備的通信線路&#xff0c;它的一個重要特征是由總線上的所有設備共享&#xff0c;因此可以將計算機系統內的多…

python基于輕量級GhostNet模型開發構建23種常見中草藥圖像識別系統

輕量級識別模型在我們前面的博文中已經有過很多實踐了&#xff0c;感興趣的話可以自行移步閱讀&#xff1a; 《移動端輕量級模型開發誰更勝一籌&#xff0c;efficientnet、mobilenetv2、mobilenetv3、ghostnet、mnasnet、shufflenetv2駕駛危險行為識別模型對比開發測試》 《基…

Vue 核心 數據監聽 computed | watch

Vue 核心 數據監聽 computed | watch 一、今日學習目標 1.指令補充 指令修飾符v-bind對樣式增強的操作v-model應用于其他表單元素 2.computed計算屬性 基礎語法計算屬性vs方法計算屬性的完整寫法成績案例 3.watch偵聽器 基礎寫法完整寫法 4.綜合案例 &#xff08;演示&…

缺陷責任期與質量保修期如何快速區分?

缺陷責任期 《建設工程質量保證金管理辦法》第二條對缺陷給出了定義&#xff0c;是指建設工程質量不符合工程建設強制性標準、設計文件&#xff0c;以及承包合同的約定。缺陷責任期是指承包人對工程質量瑕疵擔保的期限&#xff0c;由發承包雙方在合同中進行約定&#xff0c;期…

制造業數字化轉型該怎么做?這篇1.6萬字的文章終于講透了!

制造業數字化轉型該怎么做&#xff1f;下面通過 1.6W 字干貨內容&#xff0c;全面講透制造業數字化轉型。 &#xff08;為防后續找不到&#xff0c;建議大家先點贊收藏~&#xff09; 引言&#xff1a; 1、發達國家制造業回流力度加大&#xff0c;中國制造業戰略地位提升。 …