最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript異步編程常見面試題匯總

 更新時間:2023年02月08日 10:03:34   作者:腹黑的可樂  
本文將帶大家學(xué)習(xí)異步編程這一塊內(nèi)容,鑒于異步編程是js中至關(guān)重要的內(nèi)容,所以我們將學(xué)習(xí)異步編程涉及到的重點(diǎn)和難點(diǎn),同時這一塊內(nèi)容也是面試??挤秶?,希望對大家有所幫助

在上一節(jié)中我們了解了常見的es6語法的一些知識點(diǎn)。這一章節(jié)我們將會學(xué)習(xí)異步編程這一塊內(nèi)容,鑒于異步編程是js中至關(guān)重要的內(nèi)容,所以我們將會用三個章節(jié)來學(xué)習(xí)異步編程涉及到的重點(diǎn)和難點(diǎn),同時這一塊內(nèi)容也是面試??挤秶?。

并發(fā)(concurrency)和并行(parallelism)的區(qū)別

面試題 并發(fā)和并行的區(qū)別?

異步和這一小節(jié)的知識點(diǎn)其實(shí)并不是一個概念,但是這個兩個名詞確實(shí)是很多人混淆的知識點(diǎn),其實(shí)混淆的原因可能只是兩個名詞在中文的相似,在英文上來說完全是不同的單詞。

并發(fā)是宏觀概念,我分別有任務(wù)A和任務(wù)B,在一段時間內(nèi)通過任務(wù)間的切換完成了這兩個任務(wù),這種情況就可以成為并發(fā)。

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

回調(diào)函數(shù)(callback)

面試題: 什么是回調(diào)函數(shù)?回調(diào)函數(shù)有什么缺點(diǎn)?如何解決回調(diào)地獄問題?

回調(diào)函數(shù)應(yīng)該是大家經(jīng)常使用到的,以下代碼是回調(diào)函數(shù)的例子:

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

但是回調(diào)函數(shù)有個致命的弱點(diǎn),就是容易寫出回調(diào)地獄,假設(shè)多個請求存在依賴性,你可能就會寫出如下代碼:

ajax(url,()=>{
    ajax(url,()=>{})
})

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

function firstAjax(){
    ajax(url1,()=>{
        secondAjax()
    })

}
function second(){
    ajax(url2,()=>{

    })
}
ajax(url,()=>{
    firstAjax()
})

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

回調(diào)地獄得根本問題是:

  • 嵌套函數(shù)存在耦合性,一旦有改動,就會牽一發(fā)而動全身
  • 嵌套函數(shù)一多就很難處理錯誤

當(dāng)然,回調(diào)函數(shù)還存在著別的缺點(diǎn),比如不能使用try catch捕獲錯誤,不能直接return。

Generator

面試題:你理解的generator是什么?

Generator算是es6中難理解的概念之一了,Generator最大的特點(diǎn)就是可以控制函數(shù)的執(zhí)行。在這一小節(jié)中我們不會講什么是Generator,而把重點(diǎn)放在Generator的一些容易困惑的地方。

function  *foo(){
    let y = 2*(yield(x+1))
    let z = yield(y/3)
    return (x+y+z)
}
let it = foo(5)
console.log(it.next())
console.log(it.next(12))
console.log(it.next(13))

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

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

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

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

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

  • 等待中(pending)
  • 完成了 (resolved)
  • 拒絕了(rejected)

這個承諾一旦從等待狀態(tài)變成其他狀態(tài)就永遠(yuǎn)不能更改狀態(tài)了,也就是說一旦狀態(tài)編為resolved后就不能再次改變

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

當(dāng)我們在構(gòu)造 Promise 的時候,構(gòu)造函數(shù)內(nèi)部的代碼是立即執(zhí)行的

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

Promise 實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用,也就是說每次調(diào)用 then 之后返回的都是一個 Promise,并且是一個全新的 Promise,原因也是因?yàn)闋顟B(tài)不可變。如果你在 then 中 使用了 return,那么 return 的值會被 Promise.resolve() 包裝

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

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

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

前面都是在講述 Promise 的一些優(yōu)點(diǎn)和特點(diǎn),其實(shí)它也是存在一些缺點(diǎn)的,比如無法取消 Promise,錯誤需要通過回調(diào)函數(shù)捕獲。

async 及 await

面試題:async 及 await 的特點(diǎn),它們的優(yōu)點(diǎn)和缺點(diǎn)分別是什么?await 原理是什么?

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

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

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

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

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

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

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

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

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

  • 首先b先執(zhí)行,在執(zhí)行await 10之前變量a還是0,因?yàn)閍wait內(nèi)部實(shí)現(xiàn)了generator,generator會保留堆棧中東西,所以這個時候a = 0被保存下來
  • 因?yàn)閍wait是異步操作,后來的表達(dá)式不返回promise的話,就會包裝成Promise.resolve(返回值),然后去執(zhí)行函數(shù)外的同步代碼
  • 同步代碼執(zhí)行完畢后開始執(zhí)行異步代碼,將保存下來的值拿出來使用,這時候 a = 0 + 10

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

常用定時器

面試題: setTimeout,setInterval,requestAnimationFrame 各有什么特點(diǎn)?

異步編程當(dāng)然少不了定時器,常見的定時器函數(shù)有setTimeout,setInterval,requestAnimationFrame。我們先來講講最常用的setTimeout,很多人認(rèn)為setTimeout是延遲多久,那就應(yīng)該是多久后執(zhí)行。

其實(shí)這個觀點(diǎn)是錯誤的,因?yàn)閖s是單線程執(zhí)行的,如果前面的代碼影響了性能,就會導(dǎo)致setTimeout不會按期執(zhí)行。當(dāng)然了,我們可以通過代碼修正setTimeout,從而使定時器相對準(zhǔn)確

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 = interval

function loop() {
  count++
  // 代碼執(zhí)行所消耗的時間
  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)
  // 得到下一次循環(huán)所消耗的時間
  currentInterval = interval - offset 
  console.log('時:'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代碼執(zhí)行時間:'+offset, '下次循環(huán)間隔'+currentInterval) 

  setTimeout(loop, currentInterval)
}

setTimeout(loop, currentInterval)

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

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

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

以上代碼在瀏覽器環(huán)境中,如果定時器執(zhí)行過程中出現(xiàn)了耗時操作,多個回調(diào)函數(shù)會在耗時操作結(jié)束以后同時執(zhí)行,這樣可能就會帶來性能上的問題。

如果你有循環(huán)定時器的需求,其實(shí)完全可以通過 requestAnimationFrame 來實(shí)現(xiàn)

function setInterval(callback, interval) {
  let timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const 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 自帶函數(shù)節(jié)流功能,基本可以保證在 16.6 毫秒內(nèi)只執(zhí)行一次(不掉幀的情況下),并且該函數(shù)的延時效果是精確的,沒有其他定時器時間不準(zhǔn)的問題,當(dāng)然你也可以通過該函數(shù)來實(shí)現(xiàn) setTimeout。

以上就是JavaScript異步編程常見面試題匯總的詳細(xì)內(nèi)容,更多關(guān)于JavaScript異步編程的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 微信小程序保存多張圖片的實(shí)現(xiàn)方法

    微信小程序保存多張圖片的實(shí)現(xiàn)方法

    這篇文章主要介紹了微信小程序保存多張圖片的實(shí)現(xiàn)方法,使用promise 隊列,保存多張圖片到手機(jī)相冊,小編覺得具有一定的參考價值,有興趣的可以了解一下
    2019-03-03
  • 帶大家了解一下JavaScript常見的五個內(nèi)存錯誤

    帶大家了解一下JavaScript常見的五個內(nèi)存錯誤

    這篇文章主要為大家介紹了JavaScript常見的五個內(nèi)存錯誤,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • JavaScript中Require調(diào)用js的實(shí)例分享

    JavaScript中Require調(diào)用js的實(shí)例分享

    下面小編就為大家?guī)硪黄狫avaScript中Require調(diào)用js的實(shí)例分享。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • JavaScript 中的 `forEach` 無法退出循環(huán)的解決方案

    JavaScript 中的 `forEach` 無法退出循環(huán)的解決方案

    在 JavaScript 中,forEach 是一個數(shù)組方法,它用來對數(shù)組中的每個元素執(zhí)行指定的回調(diào)函數(shù),為了更好地理解這個問題,本文將通過實(shí)際項目代碼示例,并結(jié)合詳細(xì)的目錄結(jié)構(gòu)來進(jìn)行講解,感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • js如何給json對象添加、刪除、修改屬性

    js如何給json對象添加、刪除、修改屬性

    這篇文章主要介紹了js如何給json對象添加、刪除、修改屬性問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • javascript一元操作符(遞增、遞減)使用示例

    javascript一元操作符(遞增、遞減)使用示例

    javascript一元操作符遞增、遞減想必大家并不陌生吧,下面與大家分享下使用方法,有詳細(xì)學(xué)習(xí)的朋友可以了解下,希望對大家有所幫助
    2013-08-08
  • 基于ionic實(shí)現(xiàn)下拉刷新功能

    基于ionic實(shí)現(xiàn)下拉刷新功能

    這篇文章主要為大家詳細(xì)介紹了基于ionic實(shí)現(xiàn)下拉刷新功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Bootstrap按鈕組件詳解

    Bootstrap按鈕組件詳解

    按鈕組和下拉菜單組件一樣,需要依賴于button.js插件才能正常運(yùn)作。通過本文給大家詳細(xì)介紹Bootstrap按鈕組件,感興趣的朋友一起學(xué)習(xí)吧
    2016-04-04
  • 淺析JavaScript事件和方法

    淺析JavaScript事件和方法

    本文通過示例向大家簡單分析了javascript的事件和方法,文章簡潔而不簡單,是篇非常不錯的基礎(chǔ)文章,這里推薦給大家。
    2015-02-02
  • 為調(diào)試JavaScript添加輸出窗口的代碼

    為調(diào)試JavaScript添加輸出窗口的代碼

    調(diào)試JavaScript是一件很麻煩的事,盡管有很多很好用的調(diào)試工具,但有時候想要跟蹤值的變化,但即不想中斷腳本執(zhí)行,也不想用alert顯示值信息,這種情況下,一般的做法是在頁面上添加一個DIV或者其它元素,然后再往里面添加調(diào)試信息。
    2010-02-02

最新評論

临湘市| 胶南市| 海阳市| 屏边| 广灵县| 额敏县| 新巴尔虎右旗| 林口县| 哈尔滨市| 霸州市| 巧家县| 宣威市| 丘北县| 岐山县| 玛曲县| 万州区| 万山特区| 汶川县| 阳春市| 星子县| 任丘市| 湛江市| 乌兰浩特市| 康平县| 巴东县| 依兰县| 大姚县| 涿州市| 高台县| 漳平市| 炉霍县| 苗栗市| 恩施市| 曲松县| 肇源县| 永康市| 阳春市| 璧山县| 永寿县| 永登县| 阿拉善盟|