新人初步學習JAVASCIPRT時,一般都會在兩個關鍵點上犯迷糊。第一個關鍵點異步,第二個關鍵點是回調。回調 --- callback,與其對應的是調用 ---- call。
# DEMO
上一小節中使用將`function`傳入`subscribe`方法實現了:當訪問請求成功時,將請求的結果賦值給title的作用。`subscribe`方法是如何做到的呢,下面的代碼段展示了其基本原理。
```javascript
var demo = {
subscribe: function (success, error) {
// 獲取隨機值
var random = Math.floor(Math.random() * 100);
// 根據隨機值選擇執行方法
if (random % 2) {
// 并不知道success方法的作用也不關心它的作用
// 當滿足偶數時,就調用success方法。
success(random);
} else {
// 并不知道error方法的作用也不關心它的作用
// 當滿足奇數時,就調用error方法
error(random);
}
}
}
function success(randomNumber) {
console.log('我是第一個方法,接收到的值為' + randomNumber);
}
function error(randomNumber) {
console.log('我是第二個方法,接收到的值為' + randomNumber);
}
function success1(randomNumber) {
console.log('success1' + randomNumber);
}
function error1(randomNumber) {
console.log('error1' + randomNumber);
}
// 偶數時執行success, 奇數時執行error。
demo.subscribe(success, error);
// 偶數時執行success1, 奇數時執行error1。
demo.subscribe(success1, error1);
```
總結:JS支持將函數作用參數進行傳遞。被調用的方法在接收到該函數類型的參數時,決定是否調用及何時調用該參數,最終完成回調操作。這種在被調用方法中執行調用方法的模式便稱為回調。而在調用某方法時,傳入某個參數的函數便稱為回調函數。
# 生活中的回調
**調用**情景:
以前我去KFC,在前臺點了一杯3元的可樂,然后在前臺等待著可樂灌裝完成并遞給自己。接著開始喝可樂。喝完可樂后,感覺KFC環境還可以同時還有免費的WIFI。所以我又玩了會手機、刷了會微博。
對應代碼:
```
let cola = kfc.orderCola(3); ?
this.drink(cola); ?
this.playPhone(); ?
this.refreshWeibo(); ?
```
上述過程即是調用,我是調用的發起方,KFC是調用的執行方。KFC按我的要求進行處理,并將處理的結果返回給我,而在KFC處理我的訂單的過程中,我一直未離開前臺進行等待。
執行順序永遠是:????
> 有些時候,我在等待的時候還不愿意從點餐口那離開,從而無意識的耽誤了后面的顧客點餐桌,便形成了短暫的堵塞。是的,計算機的同步也存在堵塞這個問題。
**回調**情景:
你去KFC,在自動終端上點了一杯3元的可樂,自助終端給你了一張編號為123的憑證。然后你隨便找了個地坐了下來,悠閑的計劃玩玩手機,刷刷微博。KFC在可樂灌裝完成后,服務員喊道:編號123的顧客請到前臺聚餐。我停下刷微博的腳步,拿上可樂并開始享受它。
對應代碼:
```
kfc.orderCola(3, function number123(cola: Cola) { ?
this.drink(cola); ?
});
this.playPhone(); ?
this.refreshWeibo(); ?
```
上述情景中,在`服務員喊道`的時刻便發生`回調`。沒有回調以前,只能是我們主動向服務員發起調用,但有了`回調`以后,服務員便可以在完成灌裝后主動和我們打招呼了。
上述情景的執行順序為:????。在現實生活中,這個順序不是固定的。比如下次我再去買可樂的時候,餐廳的人沒有那么多了,那么KFC的服務員灌裝的時間就會小很多,所以我游戲沒玩完,人就已經通知我拿可樂了。此時,執行的順序便是????。在計算機也是這樣的,相同的上述代碼執行兩次,這兩次的執行順序也是可能不一樣的。也就是說,我們并不知道KFC的服務員最近的效率如何。最近效率高,就會通知的快一些,最近效率低就會通知的慢一些。實際生活中是這樣,在程序中也是這樣。
> 當我們向KFC服務員發起`購買`之后,KFC服務員可以在接下來在某一個時刻來`通知`我們。這個過程中的`購買`便是`調用`,而`通知`便是`回調`。
# 回調的特點
有了現實生活中活生生的例子,相信總結一下它的規律便不難了:
① 在購買可樂的時候,形成了一個契約:可樂完成后通知我(簡稱通知)。
② KFC的服務員決定什么時候通知我。
③ 不止如此,有一天我點了幾個品種想飽餐一頓,結果碰到了一個暈暈的服務員,飯都吃完了也沒有通知我去拿可樂。。。
④ 無獨有偶,還有一天我點了一懷可樂,竟然先后送了兩杯給我。
⑤ 即使點餐計劃相同,但環境不同、餐廳不同,最后整個事情的執行過程也不會相同。
⑥ 點完可樂,我們無需等待,可以選擇做其它的事情。
⑦ 點完可樂,我們也可以選擇呆呆著等著通知,什么也不做。
*****
① 在發生調用時,將回調函數(通知)做完參數傳入,形成了一個契約。
② 被調用者決定什么時候執行回調函數。
③ 回調函數可以不被執行。
④ 回調函數可以被執行多次。
⑤ 同一段包含有回調函數的代碼,每次的執行過程都可能不同。
⑥ 回調的第一種:異步回調
⑦ 回調的第二種:同步回調
> 盡管現在生活中充滿著異步。但由于計算機很傻很天真,所以點餐后如果你不主動的告訴它可以去做些別的事情了,那么它就會一直傻傻的等待下去。在JS的世界里,只有在兩種情況下回調是異步的,即:`資源請求`以及`timeout`。在后續的章節中,我們會詳細的介紹。
# 本節小測
有下述代碼:
```
kfc.orderCola(3, function number123(cola: Cola) { ?
this.drink(cola); ?
});
kfc.orderHamburger(10, function number234(hamburger: Hamburger) { ?
this.eat(hamburger); ?
}
this.playPhone(); ?
this.refreshWeibo(); ?
```
請判斷:以下執行過程是否可能發生:
1. ??????
2. ??????
3. ??????
4. ??????
5. ??????
6. ??????
7. ??????
## 上節答案
由于以下兩個代碼段等價
~~~
constructor(private httpClient: HttpClient) {
const self = this;
/* 向8080端口的helloWorld路徑發起請求 */
this.httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
function success(data: { message: string }) {
this.title = data.message;
console.log(data);
},
error);
}
~~~
~~~
export class AppComponent { ?
constructor(private httpClient: HttpClient) {
const self = this; // 常規寫法,避免在回調時發生未知的異常
/* 向8080端口的helloWorld路徑發起請求 */
this.httpClient.get('http://localhost:8080/helloWorld')
.subscribe(
success,
error);
}
title = 'hello-world'; ?
}
function success(data: { message: string }) { ?
this.title = data.message; ?
console.log(data);
}
~~~
我們心中的預期:在?處對?賦值。
實際的執行過程:在?處的`this`位于?中,所以此處的`this`指的是?,并不是?。所以?執行完畢后,?的值并不會發生任何變化。
- 序言
- 第一章:Hello World
- 第一節:Angular準備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節:Hello Angular
- 第三節:Spring Boot準備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國內源配置
- 4 package與import
- 第五節:Hello Spring Boot + Angular
- 1 依賴注入【前】
- 2 HttpClient獲取數據【前】
- 3 數據綁定【前】
- 4 回調函數【選學】
- 第二章 教師管理
- 第一節 數據庫初始化
- 第二節 CRUD之R查數據
- 1 原型初始化【前】
- 2 連接數據庫【后】
- 3 使用JDBC讀取數據【后】
- 4 前后臺對接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節 CRUD之C增數據
- 1 新建組件并映射路由【前】
- 2 模板驅動表單【前】
- 3 httpClient post請求【前】
- 4 保存數據【后】
- 5 組件間調用【前】
- 第四節 CRUD之U改數據
- 1 路由參數【前】
- 2 請求映射【后】
- 3 前后臺對接【前】
- 4 更新數據【前】
- 5 更新某個教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節 CRUD之D刪數據
- 1 綁定到用戶輸入事件【前】
- 2 刪除某個教師【后】
- 第六節 代碼重構
- 1 文件夾化【前】
- 2 優化交互體驗【前】
- 3 相對與絕對地址【前】
- 第三章 班級管理
- 第一節 JPA初始化數據表
- 第二節 班級列表
- 1 新建模塊【前】
- 2 初識單元測試【前】
- 3 初始化原型【前】
- 4 面向對象【前】
- 5 測試HTTP請求【前】
- 6 測試INPUT【前】
- 7 測試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺對接【前】
- 第三節 新增班級
- 1 初始化【前】
- 2 響應式表單【前】
- 3 測試POST請求【前】
- 4 JPA插入數據【后】
- 5 單元測試【后】
- 6 惰性加載【前】
- 7 對接【前】
- 第四節 編輯班級
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務【前】
- 4 測試間諜spy【前】
- 5 使用JPA更新數據【后】
- 6 分層開發【后】
- 7 前后臺對接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節 選擇教師組件
- 1 初始化【前】
- 2 動態數據綁定【前】
- 3 初識泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識單元測試【前】
- 7 其它問題
- 第六節 刪除班級
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺對接
- 第四章 學生管理
- 第一節 引入Bootstrap【前】
- 第二節 NAV導航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節 footer組件【前】
- 第四節 歡迎界面【前】
- 第五節 新增學生
- 1 初始化【前】
- 2 選擇班級組件【前】
- 3 復用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗【后】
- 7 唯一性校驗【后】
- 8 @PrePersist【后】
- 9 CM層開發【后】
- 10 集成測試
- 第六節 學生列表
- 1 分頁【后】
- 2 HashMap與LinkedHashMap
- 3 初識綜合查詢【后】
- 4 綜合查詢進階【后】
- 5 小試綜合查詢【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測試與分頁【前】
- 9 單選與多選【前】
- 10 集成測試
- 第七節 編輯學生
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 功能開發【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗證【前】
- 第八節 刪除學生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開發與測試【后】
- 4 集成測試
- 5 定制提示框【前】
- 6 引入圖標庫【前】
- 第九節 集成測試
- 第五章 登錄與注銷
- 第一節:普通登錄
- 1 原型【前】
- 2 功能設計【前】
- 3 功能設計【后】
- 4 應用登錄組件【前】
- 5 注銷【前】
- 6 保留登錄狀態【前】
- 第二節:你是誰
- 1 過濾器【后】
- 2 令牌機制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶登錄與注銷【后】
- 7 個人中心【前】
- 8 攔截器【后】
- 9 集成測試
- 10 單例模式
- 第六章 課程管理
- 第一節 新增課程
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 async管道【前】
- 4 優雅的測試【前】
- 5 功能開發【前】
- 6 實體監聽器【后】
- 7 @ManyToMany【后】
- 8 集成測試【前】
- 9 異步驗證器【前】
- 10 詳解CORS【前】
- 第二節 課程列表
- 第三節 果斷
- 1 初始化【前】
- 2 分頁組件【前】
- 2 分頁組件【前】
- 3 綜合查詢【前】
- 4 綜合查詢【后】
- 4 綜合查詢【后】
- 第節 班級列表
- 第節 教師列表
- 第節 編輯課程
- TODO返回機制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節 刪除課程
- 第七章 權限管理
- 第一節 AOP
- 總結
- 開發規范
- 備用