当前位置: 首页 > 后端技术 > Node.js

回调到Promise再到Async进化的初探

时间:2023-04-04 01:31:55 Node.js

题外话:今天尝试了从Markdown文件到ejs再到html文件的整个过程,这也是像Hexo一样静态博客生成过程的一部分。在这个过程中,Node中使用的fs系统恰好在写入过程中经历了从Callback到Promise再到Async的过渡。文末有福利!在Node的开发过程中,经常会遇到异步的情况。简单的说,异步就是当一个函数返回时,调用者无法得到最终的结果,需要等待一段时间才能得到。那么这个函数就可以算作一个异步函数了。在Node开发中,可以体现为资源请求,如访问数据库、读写文件等。这是一个演示代码的小例子。回调代码先:constfs=require('fs'),ejs=require('ejs'),matter=require('gray-matter'),showdown=require('showdown'),converter=newshowdown.Converter()fs.readFile('./source/hello.md','utf8',(error,data)=>{if(error){console.log(error)return}else{constmdData=matter(data)consthtml=converter.makeHtml(mdData.content)fs.readFile('./views/index.ejs','utf8',(error,data)=>{if(error){console.log(error)return}else{//ejs到htmlconsttemplate=ejs.compile(data)consthtmlStr=template({content:html})fs.writeFile('./public/index.html',htmlStr,(error)=>{if(error){console.log(error)return}else{console.log('success')}})}})}})可以看到,这只是三个读写文件,嵌套非常臃肿,你可以预见当回调多了会发生什么。我不会解释代码的作用。我们主要看回调场景。读取或写入文件后,您可以遵循回调函数。当前文件读取操作完成之后可以在回调中使用结果来执行下一次读文件和写文件,通过回调保证函数的执行顺序fs的具体使用参见Node-fs文档Promise介绍Promise后看写法:constreadFileAsync=function(path){returnnewPromise((resolve,reject)=>{fs.readFile(path,'utf8',(error,data)=>{if(error){reject(error)}else{resolve(data)}})})}constwriteFileAsync=function(path,data){returnnewPromise((resolve,reject)=>{fs.writeFile(path,data,(error,data)=>{if(error){reject(error)}else{resolve(data)}})})}lethtml=''readFileAsync('./source/hello.md').then((data1)=>{constmdData=matter(data1)html=converter.makeHtml(mdData.content)returnreadFileAsync('./views/index.ejs')}).then((data2)=>{consttemplate=ejs.compile(data2)consthtmlStr=template({content:html})returnwriteFileAsync('./public/index2.html',htmlStr)}).then(()=>console.log('success')).catch(error=>console.log(error))这里简单的使用Promise封装了fs的两个功能。以其中一个函数为例,readFileAsync返回一个Promise对象,这样就可以使用then通过这个对象回调结果。虽然封装的时候需要写一些代码,但是当有多个地方使用的时候,代码可以明显的更加简洁,而且和逐层缩进到右边是不一样的。另外一些工具库比如bluebird提供了API,可以很方便的处理异步。在以下代码或代码中先使用bluebirdAsyncawait:constfs=require('fs'),ejs=require('ejs'),matter=require('gray-matter'),showdown=require('showdown'),converter=newshowdown.Converter(),Promise=require('bluebird')Promise.promisifyAll(fs)asyncfunctionrenderHtml(){constdata1=awaitfs.readFileAsync('./source/hello.md','utf8')constmdData=matter(data1)consthtml=converter.makeHtml(mdData.content)constdata2=awaitfs.readFileAsync('./views/index.ejs','utf8')consttemplate=ejs.compile(data2)consthtmlStr=template({content:html})fs.writeFile('./public/index4.html',htmlStr)console.log('success')}renderHtml()在Node7.6及以上的函数中已经支持async,定义函数时只需要在函数前加上async关键字即可,await只能用在async函数中,通常后面跟着一个Promise对象,意思是等待Promise返回结果再继续执行。可以看到上面的函数已经很有顺序性了。当有n个异步函数回调时,只需要依次写就可以了。可见其实asyncawait也是离不开Promise的,只是写法去掉了then中callback的一丝影子,让代码更加优雅~,因为没有then,可以使用trycatch进行错误处理VSCode插件推荐的EasterEgg来了,正好结合这个例子,为了方便实时查看每一步的执行结果,推荐一款VSCode插件:Quokka。.当然在实际开发中适用性可能不是特别强,尤其是上下文依赖和后端请求依赖的场景。