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