日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JavaScript 中回调地狱的今生前世

發布時間:2024/4/14 javascript 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaScript 中回调地狱的今生前世 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 講個笑話

JavaScript 是一門編程語言

2. 異步編程

JavaScript 由于某種原因是被設計為單線程的,同時由于 JavaScript 在設計之初是用于瀏覽器的 GUI 編程,這也就需要線程不能進行阻塞。

所以在后續的發展過程中基本都采用異步非阻塞的編程模式。

簡單來說,異步編程就是在執行一個指令之后不是馬上得到結果,而是繼續執行后面的指令,等到特定的事件觸發后,才得到結果。

也正是因為這樣,我們常常會說: JavaScript 是由事件驅動的。

3. 異步實現

用 JavaScript 構建一個應用的時候經常會遇到異步編程,不管是 Node 服務端還是 Web 前端。

那如何去進行異步編程呢?就目前的標準以及草案來看,主要有下面的幾種方式:

  • 回調

  • promise

  • Generator

  • await/async

3.1 回調

這種異步的方式是最基礎的實現,如果你曾經寫過一點的 Node, 可能經常會遇到這樣的代碼:

connection.query(sql, (err, result) => {

? ?if(err) {

? ? ? ?console.err(err)

? ?} else {

? ? ? ?connection.query(sql, (err, result) => {

? ? ? ? ? ?if(err) {

? ? ? ? ? ? ? ?console.err(err)

? ? ? ? ? ?} else {

? ? ? ? ? ? ? ?...

? ? ? ? ? ?}

? ? ? ?})

? ?}

})

如此,connection.query() 是一個異步的操作,我們在調用他的時候,不會馬上得到結果,而是會繼續執行后面的代碼。這樣,如果我們需要在查到結果之后才做某些事情的話,就需要把相關的代碼寫在回調里面,如果涉及到多個這樣的異步操作,就勢必會陷入到回調地獄中去。

這種回調地獄不僅看起來很不舒服,可讀性比較差;除此之外還有比較重要的一點就是對異常的捕獲無法支持。

3.2 Promise

Promise 是 ES 2015 原生支持的,他把原來嵌套的回調改為了級聯的方式。

一般著,我們對一個 Promise 可以這樣寫:

var a = new Promise(function(resolve, reject) {

?setTimeout(function() {

? ? ?resolve('1')

?}, 2000)

})

a.then(function(val) {

? ?console.log(val)

})

如果要涉及到多個異步操作的順序執行問題,我們可以這樣寫:

var a = new Promise(function(resolve, reject) {

?setTimeout(function() {

? ? ?resolve('1')

?}, 2000)

})

?

a

?.then(function(val){

? ?console.log(val)

? ?return new Promise(function(resolve, reject) {

? ? ?setTimeout(function() {

? ? ? ? ?resolve('2')

? ? ?}, 2000)

? ?})

?})

?.then(function(val) {

? ?console.log(val)

?})

也可以把函數抽離出來

var a = new Promise(function(resolve, reject) {

?setTimeout(function() {

? ? ?resolve('1')

?}, 2000)

})

?

?

function b(val) {

?console.log(val)

?

?return new Promise(function(resolve, reject) {

? ?setTimeout(function() {

? ? ? ?resolve('2')

? ?}, 2000)

?})

}

?

?

a.then(b).then(function(val) {

console.log(val)

})

我們只需要 return 一個 Promise 即可實現這種多個異步操作的順序執行。

粗略來看,這是一個比較優雅的異步解決方案了,并且在 Promise 中我們也可以實現分級的 catch。

但對于之前接觸過其他語言的同學來說還是比較別扭的。那能否用同步的方式來書寫異步呢?

3.3 Generator

在 ES 2015 中,出現了 Generator 的語法,熟悉 Python 的同學肯定對這種語法有點了解。

簡單來說,Generator 可以理解為一個可以遍歷的狀態機,調用 next 就可以切換到下一個狀態。

在 JavaScript 中,Generator 的 function 與 函數名之間有一個 *, 函數內部使用 yield 關鍵詞,定義不同的狀態。

先看一段代碼:

function a() {

?return new Promise((resolve, reject) => {

? ?setTimeout(() => {

? ? ?resolve(1)

? ?}, 2000)

?});

};

?

var b = co(function *() {

? ?var val = yield a();

? ?console.log(val)

})

?

b()

上面的這段代碼是借助 TJ 的 co 實現的,依照約定,co 中 yield 后面只能跟 Thunk 或者 Promise.

co 的實現代碼很短,簡單來說大體是這樣:

// http://www.alloyteam.com/2015/04/solve-callback-hell-with-generator/

?

function co(genFun) {

? ?// 通過調用生成器函數得到一個生成器

? ?var gen = genFun();

? ?return function(fn) {

? ? ? ?next();

? ? ? ?function next(err, res) {

? ? ? ? ? ?if(err) return fn(err);

? ? ? ? ? ?// 將res傳給next,作為上一個yield的返回值

? ? ? ? ? ?var ret = gen.next(res);

? ? ? ? ? ?// 如果函數還沒迭代玩,就繼續迭代

? ? ? ? ? ?if(!ret.done) return ret.value(next);

? ? ? ? ? ?// 返回函數最后的值

? ? ? ? ? ?fn && fn(null, res);

? ? ? ?}

? ?}

}

簡單來說就是一直借助 generator 的 next 進行迭代,直到完成這個異步操作才返回。當前人家官方的 co 是 200 行代碼,支持異步操作的并行:

co(function *() {

? ?var val = yield [

? ? ? ?yield asyn1(),

? ? ? ?yield asyn2()

? ?]

})()

但如果我們使用 co,強迫癥們就會覺得這不是標準的寫法,有點 hack 小子的感覺。

幸運的是,在 ES 2016 的草案中,終于提出了標準的寫法。

3.4 await/async

這是在 ES 2016 中引入的新關鍵詞,這將在語言層面徹底解決 JavaScript 的異步回調問題,目前可以借助 babel 在生產環境中使用。使用 await/async 可以讓異步的操作以同步的方式來寫。

使用方法和 co 非常類似,同時也支持同步寫法的異常捕獲。

function a() {

?return new Promise((resolve, reject) => {

? ?setTimeout(() => {

? ? ?resolve(1)

? ?}, 2000)

?})

}

?

var b = async function() {

?var val = await a()

?console.log(val)

}

?

b()

如果上述的代碼完全用 Promise 實現,極有可能是下面的代碼:

function a() {

?return new Promise((resolve, reject) => {

? ?setTimeout(() => {

? ? ?resolve(1);

? ?}, 2000);

?});

};

?

var b = function() {

?a().then(val) {

? ?console.log(val)

?}

?console.log(val)

};

?

b();

相比較來說,await/async 解決了完全使用 Promise 的一個極大痛點——不同Promise之間共享數據問題:

Promise 需要設定外層數據開始共享,這樣就需要在每個then里面進行賦值,而 await/async 就不存在這樣的問題,只需要以同步的方式去寫就可以了。

await/async 對異常的支持也是特別好的:

function a() {

?return new Promise((resolve, reject) => {

? ?setTimeout(() => {

? ? ?resolve(1)

? ?}, 2000)

?});

};

?

var b = async function() {

? ?try {

? ? ? ?var val = await a()

? ? ? ?console.log(val)

? ?} catch (err) {

? ? ? ?console.log(err)

? ?}

};

?

b();

?

作者:rccoder

原文:https://github.com/rccoder/blog/issues/17

總結

以上是生活随笔為你收集整理的JavaScript 中回调地狱的今生前世的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。