路過的朋友,可以點個贊,關注一下~~~
js非同步解決的發展歷程:
回撥函式----->peomise------>generator + co----->async + await
1. 解決方案之回撥函式
回撥函式可以解決存在的非同步問題,但回撥函式分為,同步與非同步:
1.2 回撥函式可以解決非同步問題
callback
回撥函式本身是我們約定俗成的一種叫法,我們定義它,但是並不會自己去執行它,它最終被其他人執行了。
優點
:比較容易理解;缺點
:1.高耦合,維護困難,回撥地獄;2.每個任務只能指定一個回撥函式;3.如果幾個非同步操>作之間並沒有順序之分,同樣也要等待上一個操作執行結束再進行下一個操作。
下圖回撥地獄
2. 解決方案之promise的使用
2.1 promise的定義
Promise
是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。
參考
理解:
- 沒有非同步就不需要promise。
- Promise本身不是非同步,只是我們去編寫非同步程式碼的一種方式
2.2 promise 的規範
Es6將promise納入自己規範的時候,也遵循了一個相應的標準 — ``Promise A+規範。
將其歸納為4321規範
4:4大術語 3:3種狀態 2:2種事件 1:1個物件
2.2.1 四大術語
-
解決(fulfill)
:指一個 promise 成功時進行的一系列操作,如狀態的改變、回撥的執行。雖然規範中用 fulfill 來表示解決,但在後世的 promise 實現多以 resolve 來指代之。 -
拒絕(reject)
:指一個 promise 失敗時進行的一系列操作。 -
終值(eventual value)
:所謂終值,指的是 promise 被解決時傳遞給解決回撥的值,由於 promise 有一次性的特徵,因此當這個值被傳遞時,標誌著 promise 等待態的結束,故稱之終值,有時也直接簡稱為值(value)。 -
據因(reason)
:也就是拒絕原因,指在 promise 被拒絕時傳遞給拒絕回撥的值。
2.2.2 3種狀態
- 等待態(Pending)
- 執行態(Fulfilled)
- 拒絕態(Rejected)
針對每種狀態的規範
等待態(Pending)
處於等待態時,promise 需滿足以下條件:
- 可以遷移至執行態或拒絕態
執行態(Fulfilled)
處於執行態時,promise 需滿足以下條件:
- 不能遷移至其他任何狀態
- 必須擁有一個不可變的終值
拒絕態(Rejected)
處於拒絕態時,promise 需滿足以下條件:
- 不能遷移至其他任何狀態
- 必須擁有一個不可變的據因
2.2.3 2種事件
針對3種狀態,只有如下兩種轉換方向:
- pending –> fulfilled
- pendeing –> rejected
在狀態轉換的時候,就會觸發事件。
如果是
pending –> fulfiied
,就會觸發onFulFilled
事件 如果是pendeing –> rejected
,就會觸發onRejected
事件
2.2.4 1個物件
就是指promise物件
2.3 promise的基本用法
2.3.1 基本用法
let p = new Promise(function (resolve,reject) {
reject("no");
})
console.log('p :', p);
複製程式碼
回撥函式中的兩個引數,其作用就是用於轉換狀態:
resolve,將狀態從pending –> fullFilled reject,將狀態從pending –> rejected
2.3.2 then 方法
在狀態轉換的時候,就會觸發事件
。
如果是pending –> fulfiied,就會觸發onFulFilled事件
如果是pendeing –> rejected,就會觸發onRejected事件
針對事件的註冊,Promise物件提供了then
方法,如下:
2.3.3promise典型定義
2.3.3.1 案例1:讀取檔案操作
const fs = require("fs");
let p = new Promise(function (resolve,reject) {
fs.readFile("03-js基礎/a.txt","utf8",(err,date)=>{
if(err){
reject(err);
}else{
resolve(date);
}
});
});
p.then(result=>{
console.log('result :', result);
}).catch(err=>{
console.log('err :', err);
});
複製程式碼
2.3.3.2 案例2:根據隨機數返回結果
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
let num = Math.random();
if(num>0.5){
resolve("success");
}else{
reject("fail");
}
},1000);
});
p.then(result=>{
console.log('result :', result);
},err=>{
console.log('err :', err);
})
複製程式碼
2.3.3.3 讀取檔案封裝
const fs = require("fs");
function readFile(file){
return new Promise((resolve,reject)=>{
fs.readFile("file","utf8",(err,date)=>{
if(err){
reject(err);
}else{
resolve(date);
}
});
});
}
readFile("a.txt").then(result=>{
console.log('result :', result);
},err=>{
console.log('err :', err);
})
複製程式碼
2.3.4 all和race方法
all:所有,全部執行,並且滿足要求 race:競賽,誰跑的快,執行誰的(不分成功與失敗)
all和race都是Promise構造器物件的靜態方法。直接使用Promise呼叫,如下:
- Promise.all()
- Promise.reace()
- 返回值都是
promise
物件。
- 返回值都是
當有多個非同步操作的時候,經常會有如下兩種需求:(有點類似於運算子中的 邏輯與 和 邏輯或)
-
確保所有的非同步操作完成之後,才進行某個操作,只要有一個失敗,就不進行
-
只要有一個非同步操作文章,就裡面執行某個操作。
all使用如下
如有錯誤,如下
注意:
all 方法必須
所有
的非同步完全正確
,才會返回true,當其中一個出錯的時候,返回false,並且會將第一個出錯
的錯誤資訊丟擲
reac的用法
注意:
reac 方法,就是
賽跑
的意思,意思就是說,Promise.race([p1, p2, p3])裡面哪個結果獲得的快
,就返回那個結果,不管結果本身是成功狀態還是失敗狀態。
2.4 使用第三方的Promise庫
針對第三方的promise庫,有兩個知名的庫:
- bluebird
- q.js
以bluebird為例,在服務端演示其用法
。
第一步,安裝
第二步,使用
2.5 手寫最完整版的promise原理
本人在github中 逐步的封裝了基礎版promise
,升級版promise
,完整版的promise
,可以進入Github中更有層次的學習原理,
Github地址:github.com/quyuandong/…
3. 解決方案之generator 與 co
3.1 Generator的出現
ES6 增加了一項新的內容:生成器(Generator)
。生成器是一種可以從中退出並在之後重新進入的函式。生成器的環境(繫結的變數)會在每次執行後被儲存,下次進入時可繼續使用
3.2 Generator的使用
生成器的定義類似於
普通的函式,只是要加一個*
號,比如:function * g() { // 定義一個空生成器}
。yield
關鍵字是生成器函式中的亮點(只能在Generator函式中才能使用,普通函式中不可使用),其字面意思為“產出”
,每次程式執行到yield
的時候,都會“產出”一個結果。
其實Generator和 yield 兩個詞用的已經非常形象了,Generator就類似於一個工廠,每當消費者需要某種東西的時候,yield 就負責去生產,生產完了返回給消費者。
示例:
3.3 Generator的執行方式
3.4 generator和Promise、co配合使用
co庫地址:github.com/tj/co
$ npm install co
4. 解決方案之async 與 await
async/await是對Promise的優化: async/await是基於Promise的,是進一步的一種優化,不過在寫程式碼時,Promise本身的API出現得很少,很接近同步程式碼的寫法;
4.1 async關鍵字
- 1)表明程式裡面可能有非同步過程:
- 裡面可以有await關鍵字;也可以沒有(沒有表示全部同步);
- 2)非阻塞:
- async函式裡面如果有非同步過程會等待,但是async函式本身會馬上返回,不會阻塞當前執行緒,async函式內部由await關鍵字修飾的非同步過程,工作在相應的協程上,會阻塞等待非同步任務的完成再返回;
- 3)async函式返回型別為Promise物件:
- 4)無等待
- 在沒有await的情況下執行async函式,它會立即執行,返回一個Promise物件,
4.2 await關鍵字
-
1)await只能在async函式內部使用:不能放在普通函式裡面,否則會報錯;
-
2)await關鍵字後面跟Promise物件:在Pending狀態時,相應的協程會交出控制權,進入等待狀態,這是協程的本質;
-
3)await是async wait的意思: wait的是resolve(data)的訊息,並把資料data返回,
-
4)await後面也可以跟同步程式碼: 不過系統會自動將其轉化成一個Promsie物件,
4.3 簡單的使用案例:
原始碼
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, "finish");
});
}
//非同步程式碼
async function asyncTimeSys(){
await timeout(1000);
}
//呼叫方法,接收返回值
asyncTimeSys().then((value)=>{
console.log(value);
});
複製程式碼