內容目錄
前言
最近為 JavaScript 的 Callback 所苦
所以這就來個筆記把這個概念給弄清楚明白些!
(雖然說我本來是打算寫 PHP的XD)
從結論來說
Callback 基本概念不難
只是因為在實務上有太多奇怪的 code 會出現搗亂
以致有時就會很神奇的發生
有時候跳 error 跟你說哪裡錯還方便 debug 些
但有時候 javascript 就是不報錯還可以執行 只是哪裡怪怪的
那就真的是OOXX……
JavaScript Callback 是什麼?
就如何字面上表達的
是我在某段 code 裡呼叫了(call)某 function 之後,再回去(back)執行先前的 function
也就是說,寫在前面的 function 不會先執行,而是會等到被呼叫的時候才會
這有助於確保我們想要讓執行順序固定下來的情境
舉例來說
假說我們現在有兩個 function
分別是 funcA
和 funcB
如果我們想要讓執行的順序定下來是funcA -> funcB
的話
就會用到 Callback
情境1: 在 PHP 裡
由於 PHP 原則上是沒有非同步處理的機制
(有關同步與非同步,可以看這篇文章參考)
所以我們來看一下這段 code
<?php
function funcA() {
$i = rand(0.1,1);
sleep($i);
echo('function A');
};
function funcB() {
$i = rand(0.1,1);
sleep($i);
echo('function B');
};
funcA();
funcB();
/**
* result:
* function A
* function B
*/
隨機生成一個 sleep time 給funcA
和funcB
但由於 PHP 是同步處理的
程式會由上到下,一行一行處理
所以結果一定是先A後B
也就是
function A
function B
這種情況下就不會用到callback
情境2: 在 JS 裡
相較於 PHP,JS是非同處理的
所以我們來看一段類似 php 的 code
<script>
var funcA = function(){
var i = Math.random();
window.setTimeout(function(){
console.log('function A');
}, i * 1000);
};
var funcB = function(){
var i = Math.random();
window.setTimeout(function(){
console.log('function B');
}, i * 1000);
};
funcA();
funcB();
/**
* result:
* 可能是
* function A
* function B
*
* 也可能是
* function B
* function A
*/
我們一樣也生成一個隨機的 timeout 給funcA
和funcB
但在執行後
由於 JS 為非同步處理
也就是說funcB()
不會等funcA()
處理完才處理
所以最後console.log
的結果會是不確定的,可能是
function A
function B
也可能是
function B
function A
這樣就有點為難
假如說我們一定要讓funcA
先,funcB
後執行的話,那該怎麼辦呢?
這就是Callback
上場的時候啦
使用 callback 來解決 JS 非同步處理問題
來個簡單的例子
var funcA = function(callback){ // 我們在funcA裡先代一個callback參數進來
var i = Math.random(); // 這邊就都一樣
window.setTimeout(function(){
console.log('function A');
if( typeof callback === 'function' ){ // 如果callback是function的話 就執行callback();
callback();
}
}, i * 1000);
};
var funcB = function(){
var i = Math.random();
window.setTimeout(function(){
console.log('function B');
}, i * 1000);
};
// 將 funcB 作為參數帶入 funcA()
funcA( funcB );
/**
* result:
* 一定是:
* function A
* function B
*/
從上面就看的出來
如果把funcB
當作是一個參數丟到funcA
裡去,而段funcA
裡有定義Callback
的話
funcB
就會成為一個Callback
function
要等到在funcA
裡被呼叫的時候,才會被執行
實務操作
code
// 首先我們呼叫post這個function,並傳入相關參數與callback function
post(11, 22, 'Ready', '', function (data){ //區塊A
console.log(111); // output 就讓它是111吧
}, undefined, 77, 88);
function post(shopId, itemId, status, time, _afterCallback, _errorCallback, isAuto, Hash) { // 區塊B
var delayTime = 1000;
// 在post function 裡再寫一個function去call ajax,裡面也有一個completeCallback
reserveCallback(shopId, itemId, status, function(){ // 區塊C
$.ajax({
type: 'POST',
url: '/entry/ajax/update',
dataType:'json',
async:true,
data: {
'shopId':shopId,
'itemId':itemId,
'status':status,
'time':time,
'isAuto':isAuto,
'Hash':Hash
},
success: function(data){
if (_afterCallback !== undefined){
_afterCallback(data);
}
console.log(222);
},
error: function(ret){
if (_errorCallback !== undefined){
_errorCallback();
}
if (_afterCallback !== undefined){ // 關鍵A
_afterCallback();
}
console.log(333); // 關鍵B
},
complete: function(ret){
console.log(444); // 關鍵C
}
});
}, delayTime);
}
function reserveCallback(shopId, itemId, status, callback, delayTime){ // 區塊D
console.log(555);
var closureCallback = callback;
runReservedTimerId = setTimeout(function(){ // 區塊E
console.log(666);
lockCallback = true
closureCallback();
delete closureCallback;
}, delayTime);
}
說明
好的
根據之前的說明
我們的預期會印出來的東西應該是這樣吧!
555
666
333
444
111
但很不幸地,結果卻是
555
666
111
333
444
什麼情況? 不對欸~
為什麼呢?
照這樣子看來,順序是:
區塊D->區塊E->區塊A->區塊C
原因在哪?
關鍵就在於_afterCallback();
的位置
在關鍵A
的地方,我先寫了_afterCallback()
以致於程式又跳回區塊A
把function(data)
給執行完後 (印出111)
才會回來關鍵B
和關鍵C
去把這兩個333和444印出來
最後的結果才會是
555
666
111
333
444
很好! 經過此役
希望可以不用再苦於callback之中
希望有幫助到閱讀這篇文章的捧油
有什麼問題也可以留言提問喔!