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