回撥函式因為涉及的內容多而雜,並且在專案中也不怎麼使用,所以在這裡就先不說了,
本章重點講解一下 Promise
、generator + co
、async/await
因為裡面內容會有點多,並且還有好多程式碼示例。所以需要靜下心慢慢看,相信看完之後,你肯定會對這三種方法涉及的非同步問題的理解更上一層樓
如果想要大致瞭解一下的話,可以看看我的這篇文章《JS中的非同步解決方案》
我們們先說Promise
,然後慢慢涉及到其他,循序漸進(其實這是JS處理非同步的一個發展流程)
開始吧!!!
Promise
Promise簡單的說就是一個容器,裡面儲存著某個未來才會結束的時間(通常是一個非同步操作)的結果。從語法上說,Promise就是一個物件,從它可以獲取非同步操作的訊息。Promise提供統一的API,各種非同步操作都可以用同樣的方法處理。
如何理解:
- 沒有非同步就不需要promise
- promise本身不是非同步,只是我們去編寫非同步程式碼的一種方式
promise中所謂的 4 3 2 1
4大術語
一定要結合非同步操作來理解
既然是非同步,這個操作需要有個等待的過程,從操作開始,到獲取結果,有一個過程的
- 解決(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
的執行器中需要傳入兩個引數,分別是 resolve
和 reject
,在內部呼叫時,就分別代表狀態由 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
物件中呼叫resolve
和reject
時執行的函式
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
中的方法什麼時候呼叫呢?
只有當呼叫resolve
,reject
時才會去執行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.txt
和 age.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
,會作為下一個then
的promise
物件,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
如果在resolve
或reject
中又是一個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.txt
和age.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
庫可以實現自動迭代
既然是自動執行,那麼promise
的executor
中先執行一次it.next()
方法,返回value
和done
;value
是一個pending
的Promise
;如果done=false
,說明還沒有走完,繼續在value.then
的成功回撥中執行下一次next
,即呼叫read
方法;直到done
為true
,走完所有程式碼,呼叫resolve
;中間有任何一次next
異常,直接呼叫reject
,停止迭代
總結:
function
關鍵字與函式名之間有一個星號- 函式體內部使用
yield
語句,定義不同的內部狀態 yield
會將函式分割成好多個部分,每產出一次,就暫停一次Genenrator
是一個生成器,呼叫Genenrator
函式,不會立即執行函式體,只是建立了一個迭代器物件,如上例中的it
就是呼叫read
這個Generator
函式得到的一個迭代器- 迭代器有一個
next
方法,呼叫一次就會繼續向下執行,直到遇到下一個yield
或return
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
函式中使用
通常async
、await
都是跟隨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
物件await
等promise
返回結果後,在繼續執行
這三種方法都是用來解決非同步的,很很很重要
通常到公司面試的時候,面試官都會問到:
- 說一下非同步的發展流程
- 說一下非同步的解決方案
那麼看完本章內容就派上大用場了哦!
好了本章就先到此結束了,我們下期再見