来自Koa官网对于Koa的简介:javascript
koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 koa 编写 web 应用,经过组合不一样的 async函数,能够免除重复繁琐的回调函数嵌套, 并极大地提高错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得驾轻就熟。java
简而言之Koa就是基于NodeJs的Web开发框架node
Koa2相较Koa1最大的区别就是中间件的写法不一样,Koa1使用Generator,Koa2使用async/await语法。因为Koa2使用async/await语法,因此在学习以前,请使用v7.6.0+的Nodeweb
npm init npm install koa
一个简单的Hello World程序开场,shell
//index.js const Koa = require('koa') const app = new Koa() app.use( async ctx => { ctx.body = 'Hello World' }) app.listen(3000,()=>{ console.log("server is running at 3000 port"); })
node index.js
访问http://localhost:3000,页面以下所示npm
在讲解Async/Await以前,有必要简单讲一下javascript的异步发展历程,并给每种异步的方式给一段示例代码编程
异步主要经历了这么几个过程:promise
Es6以前:并发
Es6app
Es7
async function test(){ return "Hello World"; } var result=test(); console.log(result); //打印Promise { 'Hello World' }
async函数返回一个promise对象,若是在async函数中返回一个直接量,async会经过Promise.resolve
封装成Promise对象。
咱们能够经过调用promise对象的then
方法,获取这个直接量。
test().then(data=>{ console.log(data); }) //打印 "Hello World"
那如过async函数不返回值,又会是怎么样呢?
//不返回值 async function test(){ } var result=test(); console.log(result); //打印Promise { undefined }
await会暂停当前async的执行,await会阻塞代码的执行,直到await后的表达式处理完成,代码才能继续往下执行。
await后的表达式既能够是一个Promise对象,也能够是任何要等待的值。
若是await等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,而后获得 resolve 的值,做为 await 表达式的运算结果。
上边你看到阻塞一词,不要惊慌,async/await只是一种语法糖,代码执行与多个callback嵌套调用没有区别,本质并非同步代码,它只是让你思考代码逻辑的时候可以以同步的思惟去思考,避开回调地狱,简而言之-async/await是以同步的思惟去写异步的代码,因此async/await并不会影响node的并发数,你们能够大胆的应用到项目中去!
若是它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
举个例子
function A() { return "Hello "; } async function B(){ return "World"; } async function C(){ //等待一个字符串 var s1=await A(); //等待一个promise对象,await的返回值是promise对象resolve的值,也就是"World" var s2=await B(); console.log(s1+s2); } C(); //打印"Hello World"
华丽的分割线,async/await讲完了,若是你们对别的异步方式感兴趣的话,能够继续往下看,不感兴趣,到此为止啊!
回调函数就是一个参数,将这个函数做为参数传到另外一个函数里面,当那个函数执行完以后,再执行传进去的这个函数。这个过程就叫作回调,回调其实按字面意思也很好理解,先处理主函数,回头再调用做为参数传进来的这个参数,举个栗子。
function A(callback){ console.log("我是主函数"); callback(); } function B(){ console.log("我是回调函数"); } A(B); //输出结果 我是主函数 我是回调函数
Promise 对象用于一个异步操做的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。咱们常常会作些承诺,若是我赢了你就嫁给我,若是输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。)
---MDN对Promise的解释
例子:使用Promise封装fs模块中的readFile()方法
Promise构造函数的参数是一个函数,咱们把它称为处理器函数,处理器函数接收两个函数reslove
和reject
做为其参数,当异步操做顺利执行则执行reslove
函数, 当异步操做中发生异常时,则执行reject
函数。经过resolve
传入得的值,能够在then
方法中获取到,经过reject
传入的值能够在chatch
方法中获取到,
由于then
和catch
都返回一个相同的promise对象,因此能够进行链式调用。
const fs=require("fs"); //path参数是文件的路径,返回一个Promise对象 function readFileByPromise(path){ //显示返回一个Promise对象 return new Promise((resolve,reject)=>{ fs.readFile(path,"utf8",function(err,data){ if(err) reject(err); else resolve(data); }) }) } readFileByPromise("a.txt").then( data =>{ //打印文件中的内容 console.log(data); }).catch( error =>{ //抛出异常, console.log(error); })
Generator是 ES6 的新特性,中文译为生成器,在之前一个函数中的代码要么被调用,要么不被调用,还不存在能暂停的状况,Gnenerator让代码暂停成为可能,定义一个生成器很简单,在函数名前加个*****号,使用上也与普通函数有区别,看下面的例子。
一、定义生成器函数
function *Calculate(a,b){ let sum=a+b; console.log(sum); let sub=a-b; console.log(sub); }
二、建立Generator对象
Generator函数不能直接调用,直接调用Generator函数会返回一个Generator对象,只有调用Generator对象的next()
方法才能执行函数里的代码。
let gen=Calculate(2,7);
三、执行Generator函数代码
gen.next(); //打印 //9 //-5
其实单独介绍Generator并无太大的价值,要配合yield关键字,才能真正发挥Generator的价值。yield能将生Generator函数的代码逻辑分割成多个部分,下面改写上面的生成器函数。
function *Calculate(a,b){ let sum=a+b; yield console.log(sum); let sub=a-b; yield console.log(sub); } let gen=Calculate(2,7); gen.next(); //输出 //9
能够看到这段代码执行到第一个yield处就中止了,若是要让里边全部的代码都执行完就得反复调用next()方法
let gen=Calculate(2,7); //由于上边代码我用了两个yield,因此调用了两次next() gen.next(); gen.next(); //输出 //9 //-5
实现一个功能,先读取a.txt,再读取b.txt,必须按顺序读取。
const fs=require("fs"); fs.readFile("a.txt",(err,data)=>{ if(!err){ console.log(data); fs.readFile("b.txt",(err,data)=>{ if(!err) console.log(data); }) } })
这是一个典型的回调嵌套,过多的回调嵌套形成代码的可读性和可维护性大大下降,造成了使人深恶痛绝的回调地狱,试想若是有一天让你按顺序读取10个文件,那就得嵌套10层,再或者需求变动,读取顺序要变了先读b.txt,再度a.txt那改来真的不要太爽。
Generator函数的强大在于容许你经过一些实现细节来将异步过程隐藏起来,依然使代码保持一个单线程、同步语法的代码风格。这样的语法使得咱们可以很天然的方式表达咱们程序的步骤/语句流程,而不须要同时去操做一些异步的语法格式
const fs=require("fs"); function readFile(path) { fs.readFile(path,"utf8",function(err,data){ it.next(data); }) } function *main() { var result1 = yield readFile("a.txt"); console.log(result1); var result2 = yield readFile("b.txt"); console.log(result2); var result3 = yield readFile("c.txt"); console.log(result3); } var it = main(); it.next();