1. 程式人生 > >koa2教程(一)-快速開始

koa2教程(一)-快速開始

接收 框架 兩個 操作 功能 文件 一個 local pro

來自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

npm init
npm install koa

一個簡單的Hello World程序開場,

//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");
})

啟動demo

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 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到底在等什麽

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講完了,如果大家對別的異步方式感興趣的話,可以繼續往下看,不感興趣,到此為止啊!


回調函數(callback)

回調函數就是一個參數,將這個函數作為參數傳到另一個函數裏面,當那個函數執行完之後,再執行傳進去的這個函數。這個過程就叫做回調,回調其實按字面意思也很好理解,先處理主函數,回頭再調用作為參數傳進來的這個參數,舉個栗子。

function A(callback){
    console.log("我是主函數");
    callback();
}

function B(){
    console.log("我是回調函數");
}

A(B);
//輸出結果
我是主函數
我是回調函數
Promise對象

Promise 對象用於一個異步操作的最終完成(或失敗)及其結果值的表示。(簡單點說就是處理異步請求。我們經常會做些承諾,如果我贏了你就嫁給我,如果輸了我就嫁給你之類的諾言。這就是promise的中文含義:諾言,一個成功,一個失敗。)

? ---MDN對Promise的解釋

例子:使用Promise封裝fs模塊中的readFile()方法

創建一個Promise對象

? Promise構造函數的參數是一個函數,我們把它稱為處理器函數,處理器函數接收兩個函數reslovereject作為其參數,當異步操作順利執行則執行reslove函數, 當異步操作中發生異常時,則執行reject函數。通過resolve傳入得的值,可以在then方法中獲取到,通過reject傳入的值可以在chatch方法中獲取到,

? 因為thencatch都返回一個相同的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函數

Generator是 ES6 的新特性,中文譯為生成器,在以前一個函數中的代碼要麽被調用,要麽不被調用,還不存在能暫停的情況,Gnenerator讓代碼暫停成為可能,定義一個生成器很簡單,在函數名前加個*****號,使用上也與普通函數有區別,看下面的例子。

一個簡單的例子

1、定義生成器函數

function *Calculate(a,b){
  let sum=a+b;
  console.log(sum);
  let sub=a-b;
  console.log(sub);
}

2、創建Generator對象

Generator函數不能直接調用,直接調用Generator函數會返回一個Generator對象,只有調用Generator對象的next()方法才能執行函數裏的代碼。

let gen=Calculate(2,7);

3、執行Generator函數代碼

gen.next();
//打印
//9
//-5
yield關鍵字

其實單獨介紹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
Generator與異步編程

實現一個功能,先讀取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改寫上面的代碼

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(); 

koa2教程(一)-快速開始