Callback到Promise再到Async进化初探
題外:今天嘗試了一下從Markdown文件經(jīng)過(guò)ejs再到html文件的整個(gè)過(guò)程,這也是Hexo這種靜態(tài)博客生成過(guò)程中的一環(huán)。這過(guò)程中,用到的Node中的fs系統(tǒng),寫(xiě)的過(guò)程中恰好也經(jīng)歷了從Callback到Promise再到Async的轉(zhuǎn)變。文末有福利哦!
在Node開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到異步的情況,異步簡(jiǎn)單的說(shuō)就是一個(gè)函數(shù)在返回時(shí),調(diào)用者不能得到最終結(jié)果,而是需要等待一段時(shí)間才能得到,那么這個(gè)函數(shù)可以算作異步函數(shù)。那在Node開(kāi)發(fā)中具體可以體現(xiàn)為資源的請(qǐng)求,例如訪問(wèn)數(shù)據(jù)庫(kù)、讀寫(xiě)文件等等。下面舉個(gè)小例子來(lái)代碼演示一下。
Callback
先上代碼:
const fs = require('fs'),ejs = require('ejs'),matter = require('gray-matter'),showdown = require('showdown'),converter = new showdown.Converter()fs.readFile('./source/hello.md', 'utf8', (error, data) => {if (error) {console.log(error)return} else {const mdData = matter(data)const html = converter.makeHtml(mdData.content)fs.readFile('./views/index.ejs', 'utf8', (error, data) => {if (error) {console.log(error)return} else {// ejs to htmlconst template = ejs.compile(data)const htmlStr = template({content: html})fs.writeFile('./public/index.html', htmlStr, (error) => {if (error) {console.log(error)return} else {console.log('success')}})}})} })可以看到,這只是寫(xiě)了三個(gè)讀寫(xiě)文件,嵌套就顯得非常臃腫,可以預(yù)見(jiàn)到當(dāng)有更多的callback將是怎樣一個(gè)情景,代碼做了什么東西就不解釋了,主要看一下callback的場(chǎng)景,在讀或?qū)懳募罂梢愿粋€(gè)回調(diào)函數(shù),當(dāng)前一個(gè)讀文件操作完成之后,才能在回調(diào)中利用結(jié)果來(lái)執(zhí)行下一個(gè)讀文件和寫(xiě)文件,通過(guò)回調(diào)來(lái)保證函數(shù)的執(zhí)行順序。具體fs的使用,可見(jiàn)Node-fs文檔
Promise
來(lái)看看引入Promise之后的寫(xiě)法:
const readFileAsync = function (path) {return new Promise((resolve, reject) => {fs.readFile(path, 'utf8', (error, data) => {if (error) {reject(error)} else {resolve(data)}})}) } const writeFileAsync = function (path, data) {return new Promise((resolve, reject) => {fs.writeFile(path, data, (error, data) => {if (error) {reject(error)} else {resolve(data)}})}) }let html = '' readFileAsync('./source/hello.md').then((data1) => {const mdData = matter(data1)html = converter.makeHtml(mdData.content)return readFileAsync('./views/index.ejs')}).then((data2) => {const template = ejs.compile(data2)const htmlStr = template({content: html})return writeFileAsync('./public/index2.html', htmlStr)}).then(() => console.log('success')).catch(error => console.log(error))這里只是簡(jiǎn)單的用Promise封裝了一下fs的兩個(gè)函數(shù),拿其中一個(gè)函數(shù)來(lái)說(shuō),readFileAsync返回了一個(gè)Promise對(duì)象,這樣就可以通過(guò)這個(gè)對(duì)象來(lái)使用then進(jìn)行結(jié)果回調(diào),雖然在封裝的時(shí)候需要寫(xiě)一些代碼,但是當(dāng)有多處使用的時(shí)候,代碼可以明顯的簡(jiǎn)潔許多,不同再一層一層地向右縮進(jìn)。另外有一些工具庫(kù)如bluebird提供了API,可以很方便地處理異步。
在下面的代碼中使用bluebird
Async await
還是先上代碼:
const fs = require('fs'),ejs = require('ejs'),matter = require('gray-matter'),showdown = require('showdown'),converter = new showdown.Converter(),Promise = require('bluebird')Promise.promisifyAll(fs)async function renderHtml() {const data1 = await fs.readFileAsync('./source/hello.md', 'utf8')const mdData = matter(data1)const html = converter.makeHtml(mdData.content)const data2 = await fs.readFileAsync('./views/index.ejs', 'utf8')const template = ejs.compile(data2)const htmlStr = template({content: html})fs.writeFile('./public/index4.html', htmlStr)console.log('success') }renderHtml()在Node7.6以上就已經(jīng)支持async function了,定義時(shí)只需要在function之前添加async關(guān)鍵字,而await也只能在async function中使用,一般會(huì)跟一個(gè)Promise對(duì)象,表示等待Promise返回結(jié)果后,再繼續(xù)執(zhí)行。
可以看到上面的函數(shù)已經(jīng)非常順序化了,當(dāng)有n個(gè)異步函數(shù)回調(diào)時(shí),只需要順序?qū)懢涂梢岳病?梢钥闯?#xff0c;其實(shí)async await也離不開(kāi)Promise,只不過(guò)寫(xiě)法上消除了then中帶有callback的那一絲絲影子,讓代碼更加優(yōu)雅~,因?yàn)闆](méi)有了then,可以用try catch進(jìn)行錯(cuò)誤處理
VSCode插件推薦
小彩蛋來(lái)啦,正好結(jié)合這個(gè)例子,為方便實(shí)時(shí)看到每一步的執(zhí)行結(jié)果,推薦一個(gè)VSCode
插件:Quokka.ja
可以實(shí)時(shí)地進(jìn)行代碼的執(zhí)行結(jié)果,再也不用console.log之后去看終端了。當(dāng)然,在實(shí)際開(kāi)發(fā)中可能應(yīng)用性不是特別強(qiáng),尤其是對(duì)于上下文強(qiáng)依賴(lài)型、后端請(qǐng)求依賴(lài)型的場(chǎng)景。
總結(jié)
以上是生活随笔為你收集整理的Callback到Promise再到Async进化初探的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux基础 -- 命令执行顺序控制与
- 下一篇: 简简单单的正则表单验证练习