Callback到Promise再到Async進化初探

題外:今天嘗試了一下從Markdown文件通過ejs再到html文件的整個過程,這也是Hexo這種靜態博客生成過程當中的一環。這過程當中,用到的Node中的fs系統,寫的過程當中剛好也經歷了從CallbackPromise再到Async的轉變。文末有福利哦!javascript

Node開發過程當中,常常會遇到異步的狀況,異步簡單的說就是一個函數在返回時,調用者不能獲得最終結果,而是須要等待一段時間才能獲得,那麼這個函數能夠算做異步函數。那在Node開發中具體能夠體現爲資源的請求,例如訪問數據庫、讀寫文件等等。下面舉個小例子來代碼演示一下。html

Callback

先上代碼:java

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 html
        const 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文檔node

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,能夠很方便地處理異步。後端

在下面的代碼中使用bluebirdapi

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返回結果後,再繼續執行。async

能夠看到上面的函數已經很是順序化了,當有n個異步函數回調時,只須要順序寫就能夠啦。能夠看出,其實async await也離不開Promise,只不過寫法上消除了then中帶有callback的那一絲絲影子,讓代碼更加優雅~,由於沒有了then,能夠用try catch進行錯誤處理函數

VSCode插件推薦

小彩蛋來啦,正好結合這個例子,爲方便實時看到每一步的執行結果,推薦一個VSCode
插件:Quokka.ja
圖片描述
能夠實時地進行代碼的執行結果,不再用console.log以後去看終端了。固然,在實際開發中可能應用性不是特別強,尤爲是對於上下文強依賴型、後端請求依賴型的場景。

相關文章
相關標籤/搜索