深刻理解 promise、generator+co、async/await 用法

回調函數由於涉及的內容多而雜,而且在項目中也不怎麼使用,因此在這裏就先不說了,node

本章重點講解一下 Promisegenerator + coasync/awaitgit

由於裏面內容會有點多,而且還有好多代碼示例。因此須要靜下心慢慢看,相信看完以後,你確定會對這三種方法涉及的異步問題的理解更上一層樓面試

若是想要大體瞭解一下的話,能夠看看個人這篇文章《JS中的異步解決方案》npm

我們先說Promise,而後慢慢涉及到其餘,按部就班(其實這是JS處理異步的一個發展流程)數組

開始吧!!!promise

Promise

Promise簡單的說就是一個容器,裏面保存着某個將來纔會結束的時間(一般是一個異步操做)的結果。從語法上說,Promise就是一個對象,從它能夠獲取異步操做的消息。Promise提供統一的API,各類異步操做均可以用一樣的方法處理。bash

如何理解:異步

  • 沒有異步就不須要promise
  • promise自己不是異步,只是咱們去編寫異步代碼的一種方式

promise中所謂的 4 3 2 1

4大術語
必定要結合異步操做來理解
既然是異步,這個操做須要有個等待的過程,從操做開始,到獲取結果,有一個過程的async

  • 解決(fulfill)指一個 promise 成功時進行的一系列操做,如狀態的改變、回調的執行。雖然規範中用 fulfill 來表示解決,但在後世的 promise 實現多以 resolve 來指代之
  • 拒絕(reject)指一個 promise 失敗時進行的一系列操做
  • 終值(eventual value)所謂終值,指的是 promise 被解決時傳遞給解決回調的值,因爲 promise 有一次性的特徵,所以當這個值被傳遞時,標誌着 promise 等待態的結束,故稱之終值,有時也直接簡稱爲值(value)
  • 據因(reason)也就是拒絕緣由,指在 promise 被拒絕時傳遞給拒絕回調的值

3種狀態
在異步操做中,當操做發出時,須要處於等待狀態
當操做完成時,就有相應的結果,結果有兩種:編輯器

  • 成功了
  • 失敗了

一共是3種狀態,以下:

  • 等待態(Pending (也叫進行態)
  • 執行態(Fulfilled)(也叫成功態)
  • 拒絕態(Rejected) (也叫失敗態)

圖片加載失敗

針對每一種狀態,有一些規範:

等待態(Pending)
處於等待態時,promise 需知足如下條件:

  • 能夠遷移至執行態或拒絕態

執行態(Fulfilled)
處於執行態時,promise 需知足如下條件:

  • 不能遷移至其餘任何狀態
  • 必須擁有一個不可變的終值

拒絕態(Rejected)
處於拒絕態時,promise 需知足如下條件:

  • 不能遷移至其餘任何狀態
  • 必須擁有一個不可變的據因

2種事件
針對3種狀態,只有以下兩種轉換方向:

  • pending –> fulfilled
  • pendeing –> rejected

在狀態轉換的時候,就會觸發事件:

  • 若是是pending –> fulfiied,就會觸發onFulFilled事件
  • 若是是pendeing –> rejected,就會觸發onRejected事件

在調用resolve方法或者reject方法的時候,就必定會觸發事件

須要註冊onFulFilled事件 和 onRejected事件
針對事件的註冊,Promise對象提供了then方法,以下:
promise.then(onFulFilled,onRejected)

針對 onFulFilled,會自動提供一個參數,做爲終值(value)
針對 onRejected,會自動提供一個參數,做爲據因(reason)

1個對象
promise

注:只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘的操做都沒法改變這個狀態

基本使用

當咱們建立 promise 時,會默認的處於 Pending 狀態,而且在建立的時候,promise 中必定要有一個執行器,而且這個執行器會當即執行

// ()=>{} 叫執行器,會當即執行
let p = new Promise(()=>{ })
// 剛建立的Promise默認處理Pending狀態
console.log(p)  // Promise { <pending> }
複製代碼

promise 的執行器中須要傳入兩個參數,分別是 resolvereject ,在內部調用時,就分別表明狀態由 pending=>fulfilled(成功) pending=>rejected(失敗)

而且一旦 promise 狀態發生變化以後,以後狀態就不會再變了。好比:調用 resolve 以後,狀態就變爲 fulfilled,以後再調用 reject,狀態也不會變化

let p = new Promise((resolve,reject)=>{
    resolve("有錢了....")  // 如今promise就處理成功態
})
console.log(p)  // Promise { '有錢了....' }
//失敗態就不演示了
複製代碼

切記狀態發生變化以後,以後狀態就不會再變了

// 一個promise的狀態只能從等待到成功,或從等待到失敗
let p = new Promise((resolve,reject)=>{
    resolve("有錢了...")  // 成功了....
    reject("沒錢了...")  // 失敗了....
})
p.then(()=>{
    console.log("成功了....")
},()=>{
    console.log("失敗了...")
})
//只能輸出  成功了...
複製代碼

then方法

上面代碼已經看到了,在使用時能夠經過promise對象的內置方法then進行調用,then有兩個函數參數,分別表示promise對象中調用resolvereject時執行的函數

let p = new Promise((resolve,reject)=>{
    // resolve("有錢了...")  // 成功了....
    reject("沒錢了...")  //失敗了....
})
// 在then方法中,有兩個參數
// 第一個參數表示從等待狀到成功態,會調用第1個參數
// 第二個參數表示從等待狀到失敗態,會調用第2個參數
p.then(()=>{
    console.log("有錢了....")
},()=>{
    console.log("沒錢了...")
})
//輸出結果 沒錢了...
複製代碼

在執行完後,成功確定有一個成功的結果 失敗確定有一個失敗的緣由,那麼如何獲得成功的結果 ? 如何獲得失敗緣由呢?

let p = new Promise((resolve,reject)=>{
    // 調用reolve時,能夠把成功的結果傳遞下去
    // resolve("有錢了...")  // 成功了...
    // 調用reject時,能夠把失敗的緣由傳遞下去
    reject("沒錢了...")  // 失敗了....
})
p.then((suc)=>{
    console.log(suc)
},(err)=>{
    console.log(err)
})
//輸出結果  沒錢了...
複製代碼

當咱們在執行失敗處理時,也能夠用 throw ,就是拋出一個錯誤對象,也是失敗的

以下:

let p = new Promise((resolve,reject)=>{
    // throw 一個錯誤對象  也是失敗的
    throw new Error("沒錢了...")
})
p.then((suc)=>{
    console.log(suc)
},(err)=>{
    console.log(err)
})
複製代碼

throw的定義:throw語句用來拋出一個用戶自定義的異常。當前函數的執行將被中止(throw以後的語句將不會執行),而且控制將被傳遞到調用堆棧中的第一個catch塊。若是調用者函數中沒有catch塊,程序將會終止。

//嘗試一下
function getRectArea(width, height) {
  if (isNaN(width) || isNaN(height)) {
    throw "Parameter is not a number!";
  }
}
try {
  getRectArea(3, 'A');
}
catch(e) {
  console.log(e);
  // expected output: "Parameter is not a number!"
}

複製代碼

promise自己是同步的

// promise自己是同步的
console.log("start")
let p = new Promise(()=>{
    console.log("哈哈")  // 哈哈
})
console.log("end")  
//輸出順序   start  哈哈   end
複製代碼

而且在執行器的內部也是能夠寫異步代碼的

那麼then中的方法何時調用呢?
只有當調用resolvereject時纔會去執行then中的方法**

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("setTimeout")
        // resolve("有錢了...")
        reject("沒錢了...")
    },1000)
})
p.then((suc)=>{
    console.log(suc)  // 有錢了...
},(err)=>{
    console.log(err)  // 沒錢了...
})
複製代碼

鏈式調用(重點)

首先咱們在目錄下面建兩個文件,分別是:name.txtage.txt
name.txt文件裏寫了一個 age.txt
age.txt文件裏寫了一個 666

下面就以讀取文件爲例,來演示鏈式調用(須要瞭解一點node基礎)

當你讀取文件的時候,若是你用的是 vscode 編輯器,裏面會有一個小bug,用相對路徑可能會出錯,因此最好使用絕對路徑

讀取文件:

let fs = require("fs")
let path = require("path")
let filename = path.join(__dirname,"name.txt")
fs.readFile(filename,"utf8",(err,data)=>{
    if(err){
        console.log(err)
    }
    fs.readFile(path.join(__dirname,data),"utf8",(err,data)=>{
        if(err){
            console.log(err)
        }
        console.log(data)
    })
})
//輸出結果 666
複製代碼

若是用這種方法,就會出現 回調地獄 ,很難受,因此通常不用

在讀取文件時,咱們能夠專門 封裝一個函數 ,功能就是讀取一個文件的內容

let fs = require("fs")
// 封裝一個函數,函數的功能是讀取一個文件的內容
// rest參數(下去本身瞭解一下,就是能夠獲取到傳過來的全部內容)  
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
//讀文件
readFile("./name.txt","utf8").then(data=>{
    console.log(data)   
},err=>{
    console.log(err)  
})
//輸出結果  age.txt
複製代碼

若是文件不存在,會走第二個函數

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
// 若是name1不存在,走then的第2個函數
readFile("./name1.txt","utf8").then(data=>{
    console.log(data)  
},err=>{
    console.log(err)   
})
//報錯  no such file or directory
複製代碼

那麼若是咱們想要讀取age.txt裏面的內容呢?
咱們能夠這麼寫:

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
 
readFile("./name.txt","utf8").then(data=>{
    // console.log(data)  // age.txt
    readFile(data,"utf8").then(data=>{
        console.log(data)  // 666
    },err=>{
        console.log(err)  
    })
},err=>{
    console.log(err)  
})
//輸出結果  666
複製代碼

這樣寫就能夠獲取到age.txt文件裏面的內容,可是呢,這樣寫又回到了 回調地獄 ,不是說這種方法不行,而是不夠優雅

使用 鏈式調用
promise中能夠鏈式調用 就是 .then 以後,還能夠 .then ,你能夠無數次的 .then
.then 以後又返回了一個新的 promise,就是.then的函數參數中會默認返回promise對象,因此當你碰到.then連續調用的時候,你就能夠把前面的全部代碼當成一個promise

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}

readFile("./name.txt","utf8").then(data=>{
    // console.log(data)  // age.txt
    return false;
},err=>{
    console.log(err)  
}).then(data=>{  // 這裏面的data是上一個then中的第一個函數的返回值,這個.then前面的一坨代碼就能夠當成一個promise
    console.log(data)  // false
},err=>{

})
複製代碼

若是沒有這個文件,則返回錯誤信息

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
readFile("./name1.txt","utf8").then(data=>{
    return data;
},err=>{
    return err;
    // console.log(err)  
}).then(data=>{  
    console.log(data)
},err=>{
    console.log(err)
})
//輸出結果   no such file or directory
複製代碼

可是若是咱們返回一個promise呢?
那麼這個promise會執行,而且會採用他的狀態

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}

readFile("./name.txt","utf8").then(data=>{
    return data;
},err=>{
    return err;
    // console.log(err)  
}).then(data=>{  
    // console.log(data)
    return new Promise((resolve,reject)=>{	//返回一個promise
        reject("不OK")	//下面的.then採用這個狀態(失敗態)
    })
},err=>{}).then(data=>{
    console.log(data)
},err=>{
    console.log(err)  // 不OK
})
//輸出結果  不OK
複製代碼

因此若是返回的是一個promise,那麼這個promise會執行,而且會採用它的狀態

小總結:

若是在上一個then的第一個函數中,返回一個普通值,那麼不管你是在第1個函數中返回,仍是在第2個函數中返回,都會做爲下一個then的成功的結果,若是不返回,undefined就做爲下一個then的成功的結果

若是返回的是一個promise,會做爲下一個thenpromise對象,data err去promise對象中去取,也就是說,前一個then的返回值,會做爲後一個then的參數

再給兩個小例子,本身看一下:

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("hello")
    },1000)
})
p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve("world")
        },1000)
    })
}).then(data=>{
    console.log(data)  
},err=>{

})
//輸出結果 world
複製代碼
let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject("不OK")
        },1000)
    })
})
p1.then(data=>{
    console.log(data)  
},err=>{
    console.log(err) 
})
//輸出結果   不OK
複製代碼

一個坑(循環引用)

接下來講一個小問題,鏈式調用中的 循環引用

有的人不喜歡把前面的一大堆代碼後面加.then,因此就用了下面的一種寫法(有可能會出現 循環引用):

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})

let p1 = p.then(data=>{	
    return p1	//循環引用 報錯 p1在等p1的狀態改變,也就是我在等我吃飯,顯然是不行的
})

p1.then(data=>{
    console.log(data)
},err=>{
    console.log("-----",err)	//可執行,而後報錯
})
複製代碼

若是咱們把狀態肯定住,那就能夠了

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    // return 123  至關於把等待態改變成成功態
    return 123
})
p1.then(data=>{
    console.log(data)  // 123
},err=>{
    console.log("-----",err)
})
//輸出 123 
複製代碼

固然改變成失敗態也能夠

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    //  return new Error("不OK") 把等待態變成失敗態
    return new Error("不OK")
})
p1.then(data=>{
    console.log(data) 
},err=>{
    console.log(err)  // Error: 不OK
})
複製代碼

遞歸解析

看一個問題

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    resolve("666")
                },1000)
            }))
        },1000)
    })
})
// data是promise 仍是666
p1.then(data=>{
    console.log(data)  
},err=>{
    console.log(err) 
})
複製代碼

按理說,data打印出來的是一個promise,就是上面resolve裏面的一堆代碼
然而並非,promise會 進行遞歸解析.到最後上面代碼會打印出來 666

若是在resolvereject中又是一個promise,那麼就會遞歸解析(不管有多少個promise)

let p = new Promise((resolve, reject) => {
    resolve("hello")
})
let p1 = p.then(data => {
    return new Promise((resolve, reject) => {
        resolve(new Promise((resolve, reject) => {
            resolve(new Promise((resolve, reject) => {
                resolve(new Promise((resolve, reject) => {
                    resolve(new Promise((resolve, reject) => {
                        resolve(new Promise((resolve, reject) => {
                            resolve("666")
                        }))
                    }))
                }))
            }))
        }))
    })
})
p1.then(data => {
    console.log(data)
}, err => {
    console.log(err)
})
//打印結果  666
複製代碼

catch方法

  • catch方法,用於註冊 onRejected 回調
  • catch實際上是then的簡寫,then(null,callback)

catch就是.then的語法糖

若是.then中有第2個函數,在這個.then後面又有catch,若是到失敗態,那麼會走.then的第2個函數

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})

p.then(data=>{

},err=>{
    console.log("1",err)	
}).catch(err=>{
    console.log("2",err)
})
//輸出結果 1 不OK
複製代碼

若是.then中沒有第2個函數,在這個.then後面又有catch,若是到失敗態,那麼會走catch

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})
p.then(data=>{

}).catch(err=>{
    console.log("2",err)
})
//輸出結果  2 不OK
複製代碼

一個坑

.then第二個函數中,return err 這個 err 它是 return 到了下一個.then的第一個函數中

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})
p.then(data=>{

},err=>{
    // 在這裏它並無reutrn到err中,它reutrn到第一個參數中
    return err
}).then(data=>{
    console.log("data----",data)
},err=>{
    console.log("err----",err)
})
//輸出結果 data---- 不OK
複製代碼

因此最終:
一個promise中,通常在then中只有一個函數,在then後面有一個catch,通常使用then來獲取data,在catch中獲取err

let p = new Promise((resolve,reject)=>{
    resolve("OK")
})

p.then(data=>{
    console.log(data)
}).catch(err=>{
    console.log(err)
})
複製代碼

靜態方法

Pomise類上面,提供了幾個靜態方法:

  • resolve
  • reject
  • finally
  • all
  • race

resolve

Promise.resolve("有錢了...").then(data=>{
     console.log(data)  // 有錢了...
})
複製代碼

等價於下面這種寫法:

let p = new Promise((resolve,reject)=>{
    resolve("有錢了...")
})
p.then(data=>{
    console.log(data)
})
複製代碼

reject

Promise.reject("沒錢了...").catch(data=>{
    console.log(data)  // 沒錢了...
})
複製代碼

finally

無論轉成成功態仍是失敗態,都會調用finally這個方法

Promise.resolve("有錢").then(data=>{
    console.log(data)
}).finally(()=>{
    console.log("開心...")
})
//打印結果  有錢   開心...
複製代碼
Promise.reject("沒錢").catch(data=>{
    console.log(data)
}).finally(()=>{
    console.log("不開心...")
})
//打印結果  沒錢   不開心...
複製代碼

all

all表示[ ]中的promise都成功了,才能獲得最終的值

注意裏面是一個數組 讀取name.txtage.txt中的內容

let fs = require("fs").promises;
// all表示[]中的promise都成功了,才能獲得最終的值
Promise.all([fs.readFile("./name.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data) // [ 'age.txt', '666' ]
})
//打印結果  [ 'age.txt', '666' ]
複製代碼

若是有一個不成功,那麼就不行

let fs = require("fs").promises;
Promise.all([fs.readFile("./name1.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data)
})
//這個是不行的
複製代碼

race

顧名思義,race就是賽跑的意思,意思就是說,Promise.race([p1, p2, p3])裏面哪一個結果得到的快,就返回那個結果,無論結果自己是成功狀態仍是失敗狀態。

let fs = require("fs").promises;
Promise.race([fs.readFile("./name.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data) // age.txt
})
複製代碼

改變文件裏面的內容,多嘗試幾回

generator + co

我很佩服你能看到這裏,厲害

先說 生成器 和 迭代器

生成器能夠生成迭代器,可讓程序中斷,不會把 { } 中的代碼所有執行

用法:在function和本身聲名的名稱之間加一個 * 號,裏面用yield
產出數據,而後調用生成器生成迭代器

function * read(){
    yield 1;  // 只有產出,並不執行
}
//調用生成器 生成 迭代器   it就是迭代器  
let it = read()
複製代碼

生成器能夠產出不少值,迭代器只能next一下,拿一個值,next一下,拿一個值

function * read(){
    yield 1;  
}
let it = read()
console.log(it.next())  // { value: 1, done: false }
console.log(it.next())  // { value: undefined, done: true }
複製代碼
function * read(){
    yield 1;  
    yield 2;
    yield 3;
}
// 調用read()  返回值是迭代器
let it = read()
console.log(it.next())  // { value: 1, done: false }
console.log(it.next())  // { value: 2, done: false }
console.log(it.next())  // { value: 3, done: false }
console.log(it.next())  // { value: undefined, done: true }
複製代碼

若是 next 中有參數的話,那麼他會把這個參數傳給上一個生成器聲明的變量裏

因此第一個 next 中的參數沒有任何意義,咱們通常不寫

function * read(){
    let a = yield 1; 
    console.log(a)    // 9
    let b = yield 2;
    console.log(b)    // 10
    let c = yield 3;
    console.log(c)   // 11
}
let it = read()
console.log(it.next())   // { value: 1, done: false }
console.log(it.next(9))    // { value: 2, done: false }
console.log(it.next(10))   // { value: 3, done: false }
console.log(it.next(11))   // { value: undefined, done: true }
複製代碼

接下來用這個實現咱們的讀文件操做,哈哈,是否是很噁心

讀取name.txt文件

const fs = require("fs").promises;
// 生成器
function * read(){
    yield fs.readFile("./name.txt","utf-8")
}
// 迭代器
let it = read()
// console.log(it.next())  // { value: Promise { <pending> }, done: false }
it.next().value.then(data=>{	//由於是一個對象,因此直接.value
    console.log(data)  
})
//輸出結果 age.txt
複製代碼

而後讀取age.txt文件

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8")
    yield fs.readFile(concent,"utf-8")

}
let it = read()
it.next().value.then(data=>{
    // console.log(data)  
    // console.log(it.next(data)) // { value: Promise { <pending> }, done: false }
    it.next(data).value.then(data=>{
        console.log(data)  
    })
})
//輸出結果 666
複製代碼

也能夠這樣

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8")
    let age = yield fs.readFile(concent,"utf-8")
    return age
}
let it = read()
it.next().value.then(data=>{
    it.next(data).value.then(data=>{
        let r = it.next(data)
        console.log(r)  // { value: '666', done: true }
    })
})
複製代碼

是否是感受又陷入了 回調地獄

那麼就用 co 吧

安裝co npm i co

用上來:

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8")
    let age = yield fs.readFile(concent,"utf-8")
    return age
}
let co = require("co")
co(read()).then(data=>{
    console.log(data)  // 
})
//輸出結果  666
複製代碼

是否是簡單多了,爽不爽

co庫能夠實現自動迭代
既然是自動執行,那麼promiseexecutor中先執行一次it.next()方法,返回valuedonevalue是一個pendingPromise;若是done=false,說明尚未走完,繼續在value.then的成功回調中執行下一次next,即調用read方法;直到donetrue,走完全部代碼,調用resolve;中間有任何一次next異常,直接調用reject,中止迭代

總結:

  • function關鍵字與函數名之間有一個星號
  • 函數體內部使用yield語句,定義不一樣的內部狀態
  • yield會將函數分割成好多個部分,每產出一次,就暫停一次
  • Genenrator是一個生成器,調用Genenrator函數,不會當即執行函數體,只是建立了一個迭代器對象,如上例中的it就是調用read這個Generator函數獲得的一個迭代器
  • 迭代器有一個next方法,調用一次就會繼續向下執行,直到遇到下一個yieldreturn
  • next()方法能夠帶一個參數,該參數會被當作上一條yield語句的返回值,並賦值給yield前面等號前的變量
  • 每遇到一個yield,就會返回一個{value:xxx,done:bool}的對象,而後暫停,返回的value就是跟在yield後面的返回值,done表示這個generator是否已經執行結束了
  • 當遇到return時,return後的值就是value值,done此時就是true
  • 函數末尾若是沒有return,就是隱含的return undefined
  • 使用co庫,能夠自動的將generator迭代
  • co執行會返回一個promise,用then註冊成功/失敗回調
  • co將迭代器it做爲參數,這裏每調用一次read,就執行一次next

async/await

被稱爲 異步解決 的終極方案

async、await是什麼?

async顧名思義是「異步」的意思,async用於聲明一個函數是異步的。

await從字面意思上是「等待」的意思,就是用於等待異步完成。通俗來講,就是await在這裏等待promise返回結果了,再繼續執行。而且await只能在async函數中使用

一般asyncawait都是跟隨Promise一塊兒使用的。
爲何這麼說呢?由於 async返回的都是一個Promise對象,同時async適用於任何類型的函數上。這樣await獲得的就是一個Promise對象(若是不是Promise對象的話那async返回的是什麼 就是什麼);
注: await 不只僅用於等 Promise 對象,它能夠等任意表達式的結果,因此,await 後面實際是能夠接普通函數調用或者直接量的(不演示了)

緊跟着上面的代碼,再寫一段

const fs = require("fs").promises;
async function read(){
    let concent = await fs.readFile("./name.txt","utf-8")
    let age = await fs.readFile(concent,"utf-8")
    return age
}
read().then(data=>{
    console.log(data)   // 666
})
複製代碼

是否是比上面的寫法還爽呢?

await命令後面的 Promise 對象,運行結果多是 rejected,因此最好把 await 命令放在 try...catch 代碼塊中
以下:

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另外一種寫法

async function myFunction() {
  await somethingThatReturnsAPromise().catch(function (err){
    console.log(err);
  });
}
複製代碼

總結:

async:

  • async函數會返回一個Promise對象
  • 若是async函數中是return一個值,這個值就是Promise對象中resolve的值
  • 若是async函數中是throw一個值,這個值就是Promise對象中reject的值

await:

  • await只能在async函數中使用
  • await後面要跟一個promise對象
  • awaitpromise返回結果後,在繼續執行

這三種方法都是用來解決異步的,很很很重要

一般到公司面試的時候,面試官都會問到:

  • 說一下異步的發展流程
  • 說一下異步的解決方案

那麼看完本章內容就派上大用場了哦!

好了本章就先到此結束了,咱們下期再見


^_<

相關文章
相關標籤/搜索