簡體   English   中英

Node.js在回調內部調用回調函數

[英]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中,范圍解析是通過向下移動對象鏈來完成的,這些對象定義了該代碼“范圍內”的變量。 讓我們看看如何?

  • 聲明全局JavaScript變量時,實際上是在定義全局對象的屬性。
  • 在頂級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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM