来自 Koa 官网对于 Koa 的简介:
koa 是由 Express 原班人马打造的,致力于成为一个更小,更富有表现力,更健壮的 web 框架. 使用 koa 编写 web 应用,通过组合不同的 async 函数,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率.koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手.
简而言之 Koa 就是基于 NodeJs 的 Web 开发框架
Koa2 相较 Koa1 最大的区别就是中间件的写法不同,Koa1 使用 Generator,Koa2 使用 async/await 语法.由于 Koa2 使用 async/await 语法,所以在学习之前,请使用 v7.6.0 + 的 Node
Koa2 快速开始
安装 Koa2
一个简单的 Hello World 程序开场,
npm init
npm install koa
启动 demo
//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 ,页面如下所示
使用 Async/Await 语法
在讲解 Async/Await 之前,有必要简单讲一下 javascript 的异步发展历程, 并给每种异步的方式给一段示例代码
异步主要经历了这么几个过程:
Es6 之前:
回调函数(callback)
Es6
Promise 对象
Generator 函数
Es7
async/await 语法
使用 async/await
async 用于声明一个 function 是异步的
await 只能出现在用 async 修饰的 function 中
async 到底起什么作用
async 函数返回一个 promise 对象,如果在 async 函数中返回一个直接量,async 会通过 Promise.resolve 封装成 Promise 对象.
async function test(){
return "Hello World";
}
var result=test();
console.log(result);
//打印Promise { 'Hello World' }
我们可以通过调用 promise 对象的 then 方法,获取这个直接量.
那如过 async 函数不返回值,又会是怎么样呢?
test().then(data=>{
console.log(data);
})
//打印 "Hello World"
await 到底在等什么
//不返回值
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 表达式的运算结果就是它等到的东西.
举个例子
华丽的分割线,async/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"
回调函数(callback)
回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数.这个过程就叫做回调,回调其实按字面意思也很好理解,先处理主函数,回头再调用作为参数传进来的这个参数,举个栗子.
我是主函数我是回调函数
function A(callback) {
console.log("我是主函数");
callback();
}
function B() {
console.log("我是回调函数");
}
A(B);
//输出结果
Promise 对象
Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示.(简单点说就是处理异步请求.我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言.这就是 promise 的中文含义:诺言,一个成功,一个失败.)
---MDN 对 Promise 的解释
例子:使用 Promise 封装 fs 模块中的 readFile() 方法
创建一个 Promise 对象
Promise 构造函数的参数是一个函数,我们把它称为处理器函数,处理器函数接收两个函数 reslove 和 reject 作为其参数,当异步操作顺利执行则执行 reslove 函数, 当异步操作中发生异常时,则执行 reject 函数.通过 resolve 传入得的值,可以在 then 方法中获取到,通过 reject 传入的值可以在 chatch 方法中获取到,
因为 then 和 catch 都返回一个相同的 promise 对象,所以可以进行链式调用.
Generator 函数
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 让代码暂停成为可能,定义一个生成器很简单,在函数名前加个 ***** 号, 使用上也与普通函数有区别,看下面的例子.
一个简单的例子
1,定义生成器函数
2,创建 Generator 对象
function * Calculate(a, b) {
let sum = a + b;
console.log(sum);
let sub = a - b;
console.log(sub);
}
Generator 函数不能直接调用,直接调用 Generator 函数会返回一个 Generator 对象, 只有调用 Generator 对象的 next() 方法才能执行函数里的代码.
let gen = Calculate(2, 7);
3,执行 Generator 函数代码
yield 关键字
gen.next();
//打印
//9
//-5
其实单独介绍 Generator 并没有太大的价值,要配合 yield 关键字,才能真正发挥 Generator 的价值.yield 能将生 Generator 函数的代码逻辑分割成多个部分,下面改写上面的生成器函数.
可以看到这段代码执行到第一个 yield 处就停止了,如果要让里边所有的代码都执行完就得反复调用 next() 方法
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
Generator 与异步编程
let gen=Calculate(2,7);
//因为上边代码我用了两个yield,所以调用了两次next()
gen.next();
gen.next();
//输出
//9
//-5
实现一个功能,先读取 a.txt,再读取 b.txt,必须按顺序读取.
这是一个典型的回调嵌套,过多的回调嵌套造成代码的可读性和可维护性大大降低,形成了令人深恶痛绝的毁掉地狱,试想如果有一天让你按顺序读取 10 个文件,那就得嵌套 10 层,再或者需求变更,读取顺序要变了先读 b.txt, 再度 a.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);
})
}
})
使用 Generator 改写上面的代码
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();
来源: https://www.cnblogs.com/houhanbin121456/p/8297472.html