[英]Node.js calling a callback function inside a callback
我最近通過閱讀《行動》中的Node JS開始學習node.js。 這可能是一個新手問題,但是在閱讀了回調函數和變量的javascript作用域的幾篇文章之后,在本書第5章中理解此代碼背后的思想仍然有困難。
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var tasks = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err) throw err;
var data = data.toString();
var tasks = JSON.parse(data || '[]');
cb(tasks);
});
} else {
cb([]);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
});
}
它包括分為三個函數的三個回調函數。 首先調用listTasks(..),然后再調用loadorInitializeTaskArray(..)。 我的問題從這里開始,這個調用如何按節點處理? loadOrInitializeTaskArray有兩個參數,第二個是回調函數,根據簽名,它不應該接受任何參數,但是可以!
什么時候在loadorInitializeTaskArray中調用cb(..),那是什么(調用輔助函數的同一函數)?
“任務”是在函數loadOrInitializeTaskArray中聲明的數組,我們如何在listTasks(..)函數中訪問它?
我知道在Javascript中,變量的作用域位於它定義的函數和所有嵌套函數中。 但是我在這里很難理解。 那么有人可以解釋這里發生了什么嗎? 謝謝
您確實很難理解嵌套函數內部變量的范圍。 因此,讓我們開始吧。
讓我們考慮一下這段代碼
function foo(){
var a = 3;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // the value of a i.e 3 will appear on console
如果您了解C,C ++等語言,您的大腦將像這樣解釋此代碼。 如果您不知道它們中的任何一個,只需忽略這些想法。
foo()
,將聲明變量a
並為其分配值3。 foo
返回時,包含a的堆棧將被銷毀。 因此a也將被銷毀。 baz()
到底如何輸出3
。 ??? 好吧,在javascript中調用函數時,發生的事情與C中發生的事情有所不同。因此,在進一步閱讀之前,首先讓您所有的C事情都從您的腦海中消失。
在javascript中,范圍解析是通過向下移動對象鏈來完成的,這些對象定義了該代碼“范圍內”的變量。 讓我們看看如何?
因此,當執行foo()
時。 它創建一個新對象來存儲其局部變量。 因此,變量a
將存儲在該對象中。 而且,bar被定義。 定義bar時,它將存儲有效的作用域鏈。 因此,bar的作用域鏈現在包含其中具有變量a
的對象。 因此,當返回bar
時,它具有對其范圍鏈的引用。 因此,它知道a
。
因此,我想它回答了您的問題,即節點如何處理代碼。
你寫了:
loadOrInitializeTaskArray有兩個參數,第二個是回調函數,根據簽名,它不應該接受任何參數,但是可以!
回調函數是
function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
}
並且它接受一個參數tasks
。 所以,你錯了。
並且在loadOrIntializeTaskArray
,cb引用此回調函數。 cb(arg)
基本上執行此tasks = arg
,其中task是回調函數中的參數。
我想您仍然會有很多問題。 在評論中讓我知道。 並且我強烈建議您在進入節點之前通讀Core Javascript。
我不確定您的意思是“第二個是回調函數,它不應該根據簽名接受任何參數”。 回調簽名( function(tasks)
)當然確實需要參數( tasks
)。
cb
是傳入的回調函數。在這種情況下,它實際上是匿名函數:
function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
}
tasks
是在不同范圍內的兩個不同變量的名稱(但它們指向同一對象,因為數組是通過引用傳遞的)。 如前所述,一個是在loadOrInitializeTaskArray()
定義的,另一個是傳遞給loadOrInitializeTaskArray()
的回調的參數。
同樣,在不相關的注釋上,回調通常遵循“錯誤優先”格式。 這允許上游處理錯誤。 修改代碼以遵循此約定將導致:
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var tasks = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err)
return cb(err);
var data = data.toString();
// JSON.parse() throws errors/exceptions on parse errors
try {
var tasks = JSON.parse(data || '[]');
} catch (ex) {
return cb(ex);
}
cb(null, tasks);
});
} else {
cb(null, []);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(err, tasks) {
if (err) throw err;
for (var i in tasks) {
console.log(tasks[i]);
}
});
}
首先,在javascript中並沒有真正的函數簽名之類的東西。 您可以根據需要向函數傳遞盡可能多的參數。 調用該函數時,僅將loadOrInitialiseTaskArray的第二個參數分配給局部變量cb。 然后, cb(tasks)
行調用此值,因此第二個參數最好是一個函數。
當您從listTasks
調用loadOrInitializeTaskArray
時,第二個參數確實是一個函數,而該函數的第一個參數在其自己的作用域中被命名為tasks
。 它沒有達到loadOrInitializeTaskArray
並使用該函數范圍內聲明的變量。 您在調用cb
時顯式傳遞了該值。
如果我們重命名變量,代碼將相同,並且可能更易於理解:
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var taskArray = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err) throw err;
var data = data.toString();
var taskArray = JSON.parse(data || '[]');
cb(taskArray);
});
} else {
cb([]);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(listOfTasks) {
for(var i in listOfTasks) {
console.log(listOfTasks[i]);
}
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.