摘要:異步編程是每個使用編程的人都會遇到的問題,無論是前端的請求,或是的各種異步。本文就來總結(jié)一下常見的四種處理異步編程的方法。利用一種鏈式調(diào)用的方法來組織異步代碼,可以將原來以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈式調(diào)用。
異步編程是每個使用 JavaScript 編程的人都會遇到的問題,無論是前端的 ajax 請求,或是 node 的各種異步 API。本文就來總結(jié)一下常見的四種處理異步編程的方法。
回調(diào)函數(shù)使用回調(diào)函數(shù)是最常見的一種形式,下面來舉幾個例子:
// 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ù)的時候?qū)⒘硪粋€函數(shù)(回調(diào)函數(shù))作為參數(shù)傳入定義的函數(shù)當中,當異步操作執(zhí)行完畢后在執(zhí)行該回調(diào)函數(shù),從而可以確保接下來的操作在異步操作之后執(zhí)行。
回調(diào)函數(shù)的缺點在于當需要執(zhí)行多個異步操作的時候會將多個回調(diào)函數(shù)嵌套在一起,組成代碼結(jié)構(gòu)上混亂,被稱為回調(diào)地獄(callback hell)。
func1(data0, data1 => { func2(data2, data3 => { func3(data3, data4 => data4) }) })Promise
Promise 利用一種鏈式調(diào)用的方法來組織異步代碼,可以將原來以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈式調(diào)用。
// jQuery ajax promise 方式 $.get("test.html") .then(data => $(data)) .then($data => $data.find("#link").val("href")) .then(href => console.log(href))
自己定義一個 Promise 形式的函數(shù)在 ES6 當中也非常簡單:
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()))
想詳細了解 Promise 可以閱讀拙作談談 ES6 的 Promise 對象。
Generatorsnode 的著名開發(fā)者 TJ 利用 ES6 新特性生成器(Generators)開發(fā)了一個異步控制工具 co。
如果不了解 Generators 可以看看以下的文章:
深入淺出ES6(三):生成器 Generators
深入淺出ES6(十一):生成器 Generators,續(xù)篇
利用 co 可以將異步代碼的寫法寫成類似同步代碼的形式:
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 的好似顯然易見,可以使異步代碼寫得非常清晰,缺點就是要另外引入相關(guān)的庫來利用該特性。
Async/Awaitnode7.6 以上的版本引入了一個 ES7 的新特性 Async/Await 是專門用于控制異步代碼。先看一個例子:
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)鍵字定義一個包含異步代碼的函數(shù),在 Promise 形式的異步函數(shù)前面使用 await 關(guān)鍵字就可以將異步寫成同步操作的形式。
看上去與 Generators 控制方式相差不大,但是 Async/Await 是原生用于控制異步,所以是比較推薦使用的。
錯誤處理最后來探討下四種異步控制方法的錯誤處理。
回調(diào)函數(shù)回調(diào)函數(shù)錯誤處理非常簡單,就是在回調(diào)函數(shù)中同時回傳錯誤信息:
const fs = require("fs") fs.readFile("file.txt", (err, data) => { if (err) { throw err } console.log(data) })Promise
Promise 在 then 方法之后使用一個 catch 方案來捕捉錯誤信息:
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 比較類似,可以有兩種方式,第一種使用 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) } }
感謝您的閱讀,有不足之處請為我指出。
參考
談談 ES6 的 Promise 對象
深入淺出ES6(三):生成器 Generators
深入淺出ES6(十一):生成器 Generators,續(xù)篇
本文同步于我的個人博客 http://blog.acwong.org/2017/06/24/javascript-async-programming/
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/83718.html
摘要:跨域請求詳解從繁至簡前端掘金什么是為什么要用是的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。異步編程入門道典型的面試題前端掘金在界中,開發(fā)人員的需求量一直居高不下。 jsonp 跨域請求詳解——從繁至簡 - 前端 - 掘金什么是jsonp?為什么要用jsonp?JSONP(JSON with Padding)是JSON的一種使用模式,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題...
摘要:前幾天遇到一個編程題,要求控制順序執(zhí)行,今天總結(jié)了一下這個至少有好四種方法都可以實現(xiàn),包括嵌套,通過一個串起來,,實現(xiàn),以下逐一介紹。 前幾天遇到一個編程題,要求控制promise順序執(zhí)行,今天總結(jié)了一下這個至少有好四種方法都可以實現(xiàn),包括promise嵌套,通過一個promise串起來,generator,async實現(xiàn),以下逐一介紹。原題目如下: //實現(xiàn)mergePromise函...
摘要:前言看到項目里不少人用了的庫類,比如等方式,使用的時候翻看長長的文檔,真心累覺不愛。用法常用三個場景。處理異步回調(diào)多個異步函數(shù)同步處理異步依賴異步回調(diào)封裝統(tǒng)一的入口辦法或者錯誤處理處理異步回調(diào)的基本用法,處理異步回調(diào)。 前言 看到項目里不少人用了Promise 的庫類,比如 bluebird、q 、jQuery.Deffered 等 polyfill promise 方式,使用的時候...
閱讀 1179·2021-09-27 13:34
閱讀 985·2021-09-13 10:25
閱讀 513·2019-08-30 15:52
閱讀 3452·2019-08-30 13:48
閱讀 653·2019-08-30 11:07
閱讀 2174·2019-08-29 16:23
閱讀 1998·2019-08-29 13:51
閱讀 2333·2019-08-26 17:42