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以后去看终端了。固然,在实际开发中可能应用性不是特别强,尤为是对于上下文强依赖型、后端请求依赖型的场景。

相关文章
相关标签/搜索