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