摘要:異步編程是每個(gè)使用編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的請(qǐng)求,或是的各種異步。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。利用一種鏈?zhǔn)秸{(diào)用的方法來(lái)組織異步代碼,可以將原來(lái)以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈?zhǔn)秸{(diào)用。
異步編程是每個(gè)使用 JavaScript 編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的 ajax 請(qǐng)求,或是 node 的各種異步 API。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。
回調(diào)函數(shù)使用回調(diào)函數(shù)是最常見(jiàn)的一種形式,下面來(lái)舉幾個(gè)例子:
// jQuery ajax $.get("test.html", data => { $("#result").html(data) })
// node 異步讀取文件 const fs = require("fs") fs.readFile("/etc/passwd", (err, data) => { if (err) { throw err } console.log(data) })
回調(diào)函數(shù)非常容易理解,就是定義函數(shù)的時(shí)候?qū)⒘硪粋€(gè)函數(shù)(回調(diào)函數(shù))作為參數(shù)傳入定義的函數(shù)當(dāng)中,當(dāng)異步操作執(zhí)行完畢后在執(zhí)行該回調(diào)函數(shù),從而可以確保接下來(lái)的操作在異步操作之后執(zhí)行。
回調(diào)函數(shù)的缺點(diǎn)在于當(dāng)需要執(zhí)行多個(gè)異步操作的時(shí)候會(huì)將多個(gè)回調(diào)函數(shù)嵌套在一起,組成代碼結(jié)構(gòu)上混亂,被稱為回調(diào)地獄(callback hell)。
func1(data0, data1 => { func2(data2, data3 => { func3(data3, data4 => data4) }) })Promise
Promise 利用一種鏈?zhǔn)秸{(diào)用的方法來(lái)組織異步代碼,可以將原來(lái)以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈?zhǔn)秸{(diào)用。
// jQuery ajax promise 方式 $.get("test.html") .then(data => $(data)) .then($data => $data.find("#link").val("href")) .then(href => console.log(href))
自己定義一個(gè) Promise 形式的函數(shù)在 ES6 當(dāng)中也非常簡(jiǎn)單:
function ready() { return new Promise((resolve, reject) => { setTimeout(() => { resolve("ready") }, 3000) }) } ready().then(ready => console.log(`${ready} go!`))
在 node 8.0 以上的版本還可以利用 util.promisify 方法將回調(diào)形式的函數(shù)變?yōu)?Promise 形式。
const util = require("util") const fs = require("fs") const readPromise = util.promisify(fs.readFile) readPromise("test.txt").then(data => console.log(data.toString()))
想詳細(xì)了解 Promise 可以閱讀拙作談?wù)?ES6 的 Promise 對(duì)象。
Generatorsnode 的著名開(kāi)發(fā)者 TJ 利用 ES6 新特性生成器(Generators)開(kāi)發(fā)了一個(gè)異步控制工具 co。
如果不了解 Generators 可以看看以下的文章:
深入淺出ES6(三):生成器 Generators
深入淺出ES6(十一):生成器 Generators,續(xù)篇
利用 co 可以將異步代碼的寫(xiě)法寫(xiě)成類(lèi)似同步代碼的形式:
const util = require("util") const fs = require("fs") const co = require("co") const readFile = util.promisify(fs.readFile) co(function* () { const txt = yield readFile("file1.txt", "utf8") console.log(txt) const txt2 = yield readFile("file2.txt", "utf8") console.log(txt2) })
使用 Generators 的好似顯然易見(jiàn),可以使異步代碼寫(xiě)得非常清晰,缺點(diǎn)就是要另外引入相關(guān)的庫(kù)來(lái)利用該特性。
Async/Awaitnode7.6 以上的版本引入了一個(gè) ES7 的新特性 Async/Await 是專門(mén)用于控制異步代碼。先看一個(gè)例子:
const util = require("util") const fs = require("fs") const readFile = util.promisify(fs.readFile) async function readFiles () { const txt = await readFile("file1.txt", "utf8") console.log(txt) const txt2 = await readFile("file2.txt", "utf8") console.log(txt2) })
首先要使用 async 關(guān)鍵字定義一個(gè)包含異步代碼的函數(shù),在 Promise 形式的異步函數(shù)前面使用 await 關(guān)鍵字就可以將異步寫(xiě)成同步操作的形式。
看上去與 Generators 控制方式相差不大,但是 Async/Await 是原生用于控制異步,所以是比較推薦使用的。
錯(cuò)誤處理最后來(lái)探討下四種異步控制方法的錯(cuò)誤處理。
回調(diào)函數(shù)回調(diào)函數(shù)錯(cuò)誤處理非常簡(jiǎn)單,就是在回調(diào)函數(shù)中同時(shí)回傳錯(cuò)誤信息:
const fs = require("fs") fs.readFile("file.txt", (err, data) => { if (err) { throw err } console.log(data) })Promise
Promise 在 then 方法之后使用一個(gè) catch 方案來(lái)捕捉錯(cuò)誤信息:
const fs = require("fs") const readFile = util.promisify(fs.readFile) readFile("file.txt") .then(data => console.log(data)) .catch(err => console.log(err))Generators 和 Async/Await
Generators 和 Async/Await 比較類(lèi)似,可以有兩種方式,第一種使用 Promise 的 catch 方法,第二種用 try catch 關(guān)鍵字。
Promise catch
const fs = require("fs") const co = require("co") const readFile = util.promisify(fs.readFile) co(function* () { const data = yield readFile("file.txt").catch(err => console.log(err)) })
const fs = require("fs") const co = require("co") const readFile = util.promisify(fs.readFile) async function testRead() { const data = await readFile("file.txt").catch(err => console.log(err)) }
try/catch
const fs = require("fs") const co = require("co") const readFile = util.promisify(fs.readFile) co(function* () { try { const data = yield readFile("file.txt") } catch(err) { console.log(err) } })
const fs = require("fs") const readFile = util.promisify(fs.readFile) async function testRead() { try { const data = await readFile("file.txt") } catch(err) { console.log(data) } }
感謝您的閱讀,有不足之處請(qǐng)為我指出。
參考
談?wù)?ES6 的 Promise 對(duì)象
深入淺出ES6(三):生成器 Generators
深入淺出ES6(十一):生成器 Generators,續(xù)篇
本文同步于我的個(gè)人博客 http://blog.acwong.org/2017/06/24/javascript-async-programming/
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/83718.html
摘要:跨域請(qǐng)求詳解從繁至簡(jiǎn)前端掘金什么是為什么要用是的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問(wèn)的問(wèn)題。異步編程入門(mén)道典型的面試題前端掘金在界中,開(kāi)發(fā)人員的需求量一直居高不下。 jsonp 跨域請(qǐng)求詳解——從繁至簡(jiǎn) - 前端 - 掘金什么是jsonp?為什么要用jsonp?JSONP(JSON with Padding)是JSON的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問(wèn)的問(wèn)題...
摘要:前幾天遇到一個(gè)編程題,要求控制順序執(zhí)行,今天總結(jié)了一下這個(gè)至少有好四種方法都可以實(shí)現(xiàn),包括嵌套,通過(guò)一個(gè)串起來(lái),,實(shí)現(xiàn),以下逐一介紹。 前幾天遇到一個(gè)編程題,要求控制promise順序執(zhí)行,今天總結(jié)了一下這個(gè)至少有好四種方法都可以實(shí)現(xiàn),包括promise嵌套,通過(guò)一個(gè)promise串起來(lái),generator,async實(shí)現(xiàn),以下逐一介紹。原題目如下: //實(shí)現(xiàn)mergePromise函...
摘要:詳解十大常用設(shè)計(jì)模式力薦深度好文深入理解大設(shè)計(jì)模式收集各種疑難雜癥的問(wèn)題集錦關(guān)于,工作和學(xué)習(xí)過(guò)程中遇到過(guò)許多問(wèn)題,也解答過(guò)許多別人的問(wèn)題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實(shí)現(xiàn)方式 延遲加載也稱為惰性加載,即在長(zhǎng)網(wǎng)頁(yè)中延遲加載圖像。用戶滾動(dòng)到它們之前,視口外的圖像不會(huì)加載。本文詳細(xì)介紹了三種延遲加載的實(shí)現(xiàn)方式。 詳解 Javascript十大常用設(shè)計(jì)模式 力薦~ ...
摘要:前言看到項(xiàng)目里不少人用了的庫(kù)類(lèi),比如等方式,使用的時(shí)候翻看長(zhǎng)長(zhǎng)的文檔,真心累覺(jué)不愛(ài)。用法常用三個(gè)場(chǎng)景。處理異步回調(diào)多個(gè)異步函數(shù)同步處理異步依賴異步回調(diào)封裝統(tǒng)一的入口辦法或者錯(cuò)誤處理處理異步回調(diào)的基本用法,處理異步回調(diào)。 前言 看到項(xiàng)目里不少人用了Promise 的庫(kù)類(lèi),比如 bluebird、q 、jQuery.Deffered 等 polyfill promise 方式,使用的時(shí)候...
閱讀 1258·2021-09-27 13:34
閱讀 1072·2021-09-13 10:25
閱讀 571·2019-08-30 15:52
閱讀 3513·2019-08-30 13:48
閱讀 744·2019-08-30 11:07
閱讀 2231·2019-08-29 16:23
閱讀 2057·2019-08-29 13:51
閱讀 2392·2019-08-26 17:42