下面是凱爾·辛普森 (Kyle Simpson) 的“異步與性能”一書中的一個示例。
試圖理解下面的代碼,但我有問題 1) 傳入 fetchX 和 fetchY 有什么意義?2) getX 和 getY 如何運行?我可以想象使用 getX() 和 getY() 運行,但代碼有 getX(){} 和 getY(){},這看起來更像函式宣告?我在這里錯了嗎?
function add(getX,getY,cb) {
var x, y;
getX( function(xVal){
x = xVal;
if (y != undefined) {
cb(x y );
}
});
getY( function(yVal){
y = yVal;
if (x != undefined) {
cb(x y);
}
})
}
// fetchX() and fetch() are sync or async functions
add(fetchX, fetchY, function(sum) {
console.log(sum);
});
uj5u.com熱心網友回復:
要回答您的問題,我們可以分析代碼示例。如果我們從上到下和從左到右閱讀代碼(請注意,盡管這是決議代碼的方式,但這不是執行代碼的方式,但我們會回到那個),我們首先看到
function add(getX,getY,cb) {
var x, y;
// ...
}
這是一個名為 的函式的宣告add,有 3 個引數getX,分別為getY和cb。
函式體內的第一行是
var x, y;
var函式體內的關鍵字意味著這是一個函式范圍的變數宣告。這只是意味著xand是從函式體y的開始到結尾宣告的(關鍵字也可以用于全域范圍的變數宣告,還有塊范圍的變數宣告的關鍵字)。addvarlet
下一個代碼部分是
getX( function(xVal){
x = xVal;
if (y != undefined) {
cb(x y );
}
});
這從呼叫開始getX(...);(記住getX是我們所在函式的第一個引數add)。所以getX必須是我們可以呼叫的東西,即一個函式,否則我們會得到一個運行時錯誤。
getX傳遞給呼叫的第一個(也是唯一的)輸入引數是
function(xVal){
x = xVal;
if (y != undefined) {
cb(x y );
}
}
這是一個函式運算式。
function 關鍵字可用于在運算式中定義函式。
以這種方式使用function關鍵字(或使用箭頭函式運算式)而不指定名稱,定義了一個匿名函式。您可能會聽到其他人將它們(取決于背景關系)稱為callbacks/lambdas/closures。
所以這告訴我們該getX函式應該接受一個函式作為第一個引數,并可能將其作為其內部邏輯的一部分來呼叫。
我們還注意到這個匿名函式function(xVal){ ... }接受一個引數xVal。這告訴我們,getX當(如果)它呼叫這個函式時,函式應該傳遞一些值作為第一個引數。
因此,如果該getX函式曾經回呼我們作為其第一個引數傳遞的這個匿名函式,那么這個匿名函式的主體將被執行。如果getX函式從不呼叫它,那么這個匿名函式的主體將永遠不會被執行。
匿名函式的主體是
x = xVal;
if (y != undefined) {
cb(x y );
}
它只是分配xVal給x并有條件地呼叫cb(x y),即add函式的第三個引數,這里仍然是外部函式范圍。
我們可以對getY通話進行類似的細分。唯一的區別是它呼叫getY函式引數并且匿名函式的主體分配yVal給 y (但它仍然有條件地呼叫cb(x y))。
代碼示例的最后幾行是
// fetchX() and fetch() are sync or async functions
add(fetchX, fetchY, function(sum) {
console.log(sum);
});
So this is a call of the previously declared named function function add(getX, getY, cb) { ... }.
When this code line is executed it will call add with
- 1st parameter:
fetchX - 2nd parameter:
fetchY - 3rd parameter: an anonymous function
function(sum) { console.log(sum); }. This anonymous function accepts a single parametersumand only when (if) called it logs it to the console.
- what is the point of passing in fetchX and fetchY?
As we see from the breakdown above, fetchX and fetchY are passed into the add function call in order for them to serve as "replacements" for calls to getX and getY inside the function body. You can think of it that the call to getX(...) calls fetchX(...) and the call to getY(...) calls fetchY(...).
They will be "called back" from inside the function, that's why in such cases we call them callbacks, they are functions passed as parameters into another function, and said other function might call them based on its internal logic. In your example they are called once each (assuming no errors happen during runtime).
- How does getX and getY run? I can imagine running w/ getX() and getY() but code has getX(){} and getY(){} and that looks more like function declaration?
As mentioned earlier the code is not always executed from top to bottom and from left to right. So after parsing the code from the example, the code interpreter will execute the add call first (because the rest is just a declaration for the add function) and it will pass fetchX, fetchY and function(sum){ console.log(sum); } as the parameters. Right after the call happens the execution continues with the add function body.
Right from the getX call inside the add function body there are different possible flows, depending on what the logic inside the getX and getY is (specifically fetchX and fetchY in your case). Since we don't have a documentation for fetchX and fetchY nor their source code, we cannot know how the code will execute from here on. But we can explore some of the possible flows.
I added a few console.log statements to your original code example. In that way we can see the order of the execution. Another way to do this, without the need to add extra log statements, is to use the debugger, place a breakpoint somewhere inside the function and then use the step commands to step through the code as it executes.
Option 1: If both fetchX and fetchY call their first parameter (the callback they receive) always exactly once synchronously then the flow is (run the code snippet)
// These two callbacks could be defined elsewhere, maybe even in code not under our control, such as in a library...
fetchX = function(callback) {
callback(2); // unconditional single synchronous call
};
fetchY = function(callback) {
callback(3); // unconditional single synchronous call
};
console.log("Entering the code example scope");
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y;
getX( function(xVal){
console.log("Called: function(xVal) xVal =", xVal);
x = xVal;
if (y != undefined) {
cb(x y);
}
});
getY( function(yVal){
console.log("Called: function(yVal) yVal =", yVal);
y = yVal;
if (x != undefined) {
cb(x y);
}
});
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Option 2: If fetchX calls its callback always exactly once synchronously and fetchY calls its callback parameter always exactly once asynchronously, then the flow is (run the code snippet)
fetchX = function(callback) {
callback(2); // unconditional single synchronous call
};
fetchY = function(callback) {
setTimeout(function() { callback(3); }, 0); // unconditional single asynchronous call
};
console.log("Entering the code example scope");
// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x y); } }); getY( function(yVal){ console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x y); } });
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Option 3: If fetchX calls its callback always exactly once asynchronously and fetchY calls its callback parameter always exactly once synchronously, then the flow is (run the code snippet)
fetchX = function(callback) {
setTimeout(function() { callback(2); }, 0); // unconditional single asynchronous call
};
fetchY = function(callback) {
callback(3); // unconditional single synchronous call
};
console.log("Entering the code example scope");
// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x y); } }); getY( function(yVal){ console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x y); } });
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Option 4: If both fetchX and fetchY call their callback always exactly once asynchronously and fetchX calls it sooner, then the flow is (run the code snippet)
fetchX = function(callback) {
setTimeout(function() { callback(2); }, 0); // unconditional single asynchronous call
};
fetchY = function(callback) {
setTimeout(function() { callback(3); }, 50); // unconditional single asynchronous call (further delayed)
};
console.log("Entering the code example scope");
// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x y); } }); getY( function(yVal){ console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x y); } });
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Option 5: If both fetchX and fetchY call their callback always exactly once asynchronously and fetchY calls it sooner, then the flow is (run the code snippet)
fetchX = function(callback) {
setTimeout(function() { callback(2); }, 50); // unconditional single asynchronous call (further delayed)
};
fetchY = function(callback) {
setTimeout(function() { callback(3); }, 0); // unconditional single asynchronous call
};
console.log("Entering the code example scope");
// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x y); } }); getY( function(yVal){ console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x y); } });
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Option 6: If fetchX calls its callback exactly three times asynchronously on an interval of 30ms with values 10, 20, 30 and fetchY calls its callback exactly three times asynchronously on an interval of 50ms with values 1, 2, 3, then the flow is (run the code snippet)
fetchX = function(callback) {
setTimeout(function() { callback(10); }, 30);
setTimeout(function() { callback(20); }, 60);
setTimeout(function() { callback(30); }, 90);
};
fetchY = function(callback) {
setTimeout(function() { callback(1); }, 50);
setTimeout(function() { callback(2); }, 100);
setTimeout(function() { callback(3); }, 150);
};
console.log("Entering the code example scope");
// add function is same as before, just in one line for space purposes
function add(getX,getY,cb) {
console.log("Called: function add(getX,getY,cb)");
var x, y; getX( function(xVal){ console.log("Called: function(xVal) xVal =", xVal); x = xVal; if (y != undefined) { cb(x y); } }); getY( function(yVal){ console.log("Called: function(yVal) yVal =", yVal); y = yVal; if (x != undefined) { cb(x y); } });
console.log("Returning from: function add(getX,getY,cb)");
}
add(fetchX, fetchY, function(sum) {
console.log("Called: function(sum)");
console.log(sum);
console.log("Returning from: function(sum)");
});
console.log("Leaving the code example scope");
Here are the outputs of the above options
Option 1:
Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(xVal) xVal = 2
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Option 2:
Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(xVal) xVal = 2
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)
Option 3:
Entering the code example scope
Called: function add(getX,getY,cb)
Called: function(yVal) yVal = 3
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 2
Called: function(sum)
5
Returning from: function(sum)
Option 4:
Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 2
Called: function(yVal) yVal = 3
Called: function(sum)
5
Returning from: function(sum)
Option 5:
Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(yVal) yVal = 3
Called: function(xVal) xVal = 2
Called: function(sum)
5
Returning from: function(sum)
Option 6:
Entering the code example scope
Called: function add(getX,getY,cb)
Returning from: function add(getX,getY,cb)
Leaving the code example scope
Called: function(xVal) xVal = 10
Called: function(yVal) yVal = 1
Called: function(sum)
11
Returning from: function(sum)
Called: function(xVal) xVal = 20
Called: function(sum)
21
Returning from: function(sum)
Called: function(xVal) xVal = 30
Called: function(sum)
31
Returning from: function(sum)
Called: function(yVal) yVal = 2
Called: function(sum)
32
Returning from: function(sum)
Called: function(yVal) yVal = 3
Called: function(sum)
33
Returning from: function(sum)
當然還有更多可能的流量。任何一個函式都可以同步或異步多次呼叫它的回呼(不僅僅是一次),或者它可以在一個間隔上永遠呼叫它,或者它可以有條件地呼叫它——所以有時甚至一次都不會。
從上面的所有可能性中可以看出,如果沒有函式的檔案或源代碼fetchX,fetchY就不可能確定流程是什么。此外,流程將根據傳入的函式動態變化。
您可以復制上面的任何代碼片段并更改其中的邏輯fetchX,fetchY然后自己探索您感興趣的其他流程。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/414260.html
標籤:
