刘流的博客

KOA 基础

Word count: 1.5kReading time: 6 min
2020/08/19 Share

一:KOA简介

KOA 的特点:

  1. 洋葱模型

  2. 精简(定制化的能力很强,KOA 需要二次开发。)

二:初始化项目

  1. 在项目的根目录终端输入命令: npm init 初始化一个 Node.js 项目了。
  2. 安装 KOA:npm i koa

三:导入导出的方式

  1. commonJS
  2. ES6
  3. AMD

nodeJS 里不能直接使用 ES6 的导入与导出,因为 ES6 在 NodeJS 里面还是实验特性。NodeJs 也不支持直接在类上面定义属性。

1
2
3
4
5
6
7
8
class test{
// node 不支持直接在类里面直接定义属性
x = 1
constructor(){
// 只能通过构造器的方式定义属性
this.x = 1
}
}

假入我们需要使用这些 ES6 的新特性的话,可以通过 babel 在 Node 里实现 ES 的新的特性。但是配置起来很复杂。

四:使用 KOA

  1. 导入 KOA
    1
    const koa = require('koa');
  2. 实例化 KOA
    1
    const app = new Koa()

3.中间件
中间件就是函数,定义一个中间件就是定义一个函数。定义一个中间件之后要注册到函数上面。使用中间件:
示例代码:

1
2
3
4
5
6
7
8
9
const Koa = require('koa')
const app = new Koa()

function test(){
console.log('test1')
}

app.use(test)
app.listen(3222)

当我们定义了两个中间件之后,KOA 只会默认执行第一个中间件。在 app.use() 里面传入的函数里面有两个参数,第一个参数的 ctx 是上下文的意思,第二个参数 next 的指的是下一个 KOA 中间件.当我们在中间件函数里面调用了 next() 之后,KOA 就会继续调用下一个中间件函数。

1
2
3
4
5
6
7
8
9
app.use((ctx,next)=>{
console.log('test2')
next()
})

app.use((ctx,next)=>{
console.log('test3')
})

  1. 洋葱模型
    如果不强制加上 async 和 await 的话,我们难以保证代码是按照洋葱模型的顺序执行的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Koa = require('koa')
const app = new Koa()

app.use( async (ctx,next)=>{
console.log(1)
await next()
console.log(2)
})

app.use( async (ctx,next)=>{
console.log(3)
await next()
console.log(4)
})

app.listen(3222)

// 输出顺序是:1 3 4 2
  1. 深入理解 async 和 await

思考: next() 返回的结果是什么?

1
2
3
const a = next()
// next() 的返回结果是一个 Promise
console.log(a) // Promise { undefined }

Promise 对象的值是 undefined 的原因是因为下一个中间件没有返回任何值。当下一个中间件有返回值的话,Promise的内容就是该中间件返回的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
app.use(  (ctx,next)=>{
console.log(1)
const a = next() //Promise { 'hello Promise' }
console.log(a)
console.log(2)
})

app.use((ctx,next)=>{
console.log(3)
next()
console.log(4)
return 'hello Promise'
})

当需要获取 Promise 返回的内容时,使用 Promise.then() 方法。若获取 Promise { ‘abc’ } 里面的 ‘abc’ 则使用 Promise.then() 获取,

1
2
3
4
// Promise.then()的使用方法。
a.then((res)=>{
console.log(res)
})

每一次都使用 Promise.then() 方法有点麻烦,有没有另一种方法获取异步返回的结果呢?这时我们使用异步的终极解决方案 async await。

1
2
3
4
app.use( async (ctx,next)=>{
const a = await next()
console.log(a)
})

五:理解 async 和 await

await 可以看作是求值关键字。await 有等待的意思,其会阻塞线程。await 是不是只能对 Promise 进行求值呢?不是的。await 可以对表达式进行求值,不仅仅只能对 Promise 进行求值。只不过大多数情况下我们只是对 Promise 进行求值的。

1
2
3
4
5
6
const sum = async ()=>{
const num = await 100*100;
console.log(num)
}
// IDE 会提示使用 await 多余。
sum() // 10000

await 会阻塞线程。
什么样子的代码是异步的呢?

  1. 对资源的操作一般都是异步的(读文件)
  2. 发送 http 请求
  3. 操作数据库

下面用两段代码来演示 await 阻塞线程

1
2
3
4
5
6
7
app.use((ctx,next)=>{
const axios = require('axios')
const start = Date.now()
const res = axios.get('https://www.imooc.com/t/4294850')
const end = Date.now()
console.log(end-start) // 0
})

可以看到,在不使用 async 和 await 的情况下,http 请求的时间几乎为 0

1
2
3
4
5
6
7
app.use(async (ctx,next)=>{
const axios = require('axios')
const start = Date.now()
const res = await axios.get('https://www.imooc.com/t/4294850')
const end = Date.now()
console.log(end-start) // 756
})

而在使用 await 的 async 的情况下,却发送了阻塞。

阻塞线程不是会运行卡断,线程被阻塞了,不是说就不运行代码了,而是去运行其他的代码了。

async 的意义,在一个函数的前面加上 async 的时候,这个函数在任何时候的返回值都会被包装成一个 Promise.

1
2
3
4
5
6
async function f() {
return 'hello'
}

console.log(f()) // Promise { 'hello' }

只有在中间件上面加上 async 和 await 才能保证洋葱模型

如果不加上async await的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
app.use((ctx,next)=>{
console.log(1)
next()
console.log(2)
})
app.use(async (ctx,next)=>{
console.log(3)
const axios = require('axios')
const res = await axios.get('https://www.imooc.com/t/4294850')
next()
console.log(4)
})
// 1 3 2 4

加上之后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.use(async (ctx,next)=>{
console.log(1)
await next()
console.log(2)
})

app.use(async (ctx,next)=>{
console.log(3)
const axios = require('axios')
const res = await axios.get('https://www.imooc.com/t/4294850')
next()
console.log(4)
})

// 1 3 4 2

洋葱模型的作用:比如我们写了很多个中间件,需要在第一个中间件里面写一个函数,需要等到其他的中间件全部运行完了之后才运行(比如计时),那么我们就必须要让中间件按照洋葱模型来运行才可以正常完成这个功能。

还比如获取 ctx 上面的内容需要用到洋葱模型。

1
2
3
4
5
6
7
8
9
10
11
12
app.use(async (ctx,next)=>{
await next()
const r = ctx.x
console.log(r)
})

app.use(async (ctx,next)=>{
const axios = require('axios')
const res = await axios.get('https://www.imooc.com/t/4294850')
next()
ctx.x = res
})
CATALOG
  1. 1. 一:KOA简介
  2. 2. 二:初始化项目
  3. 3. 三:导入导出的方式
  4. 4. 四:使用 KOA
  5. 5. 五:理解 async 和 await