2015-11-24 25 views
1

我很新的節點和JS,我還沒有收到有關的回調和異步編程直到昨天的想法,所以我就像個白癡和我說話,「因爲我是...我在想回調是正確的嗎?

隨着混合物的死亡。我以爲我會寫我自己的小靜態網站建設者。我看着咕嘟咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕咕嚕叫。

構建CSS,縮小,上市等等非常簡單,但是當涉及到構建頁面時,生活很快就陷入了回調地獄。

的閱讀了一下,我對網頁腳本建築開始:

var fm   = require('front-matter'), 
    fs   = require('fs'), 
    glob  = require('glob'), 
    md   = require('marked'); 

const SEARCHPATH = "content/pages/"; 


pages = []; 



function searchFiles() { 
    glob("*.md", { cwd: SEARCHPATH }, readFiles); 
} 


function readFiles (err, files) { 
    if(err) throw err; 

    for (var file of files) { 
     fs.readFile(SEARCHPATH + file, 'utf8', processFiles); 
    } 
} 


function processFiles(err, data) { 
    if(err) throw err; 

    var attributes = fm(data).attributes; 
    var content = md(fm(data).body); 

    pages.push(attributes, content); 

    applyTemplate(pages); 
} 

function applyTemplate(pages) { 
    console.log(pages); 
} 

searchFiles(); 

但它看起來真像我即將陷入菊花鏈地獄,每個函數調用接下來,但是我不能在不這樣做的情況下訪問頁面變量。

這一切似乎有點關閉。

我在想這個權利嗎?以編程方式構建這個更好的方法是什麼?

非常感謝Overflowers。

+0

你已經完成了一個很好的工作,使你的結構扁平化並使其可讀。你正在創建你自己的模塊嗎?這是保持您的代碼清潔和各自關切的另一件好事。 – lintmouse

+0

除非您正在執行異步操作的回調,否則不需要每個函數都可以調用下一個函數並越來越深地進入堆棧。其他代碼可以看起來像你喜歡的。 –

+0

感謝dustmouse。不,我不是。你會推薦作爲下一步嗎?我確信我沒有考慮這個權利:我仍然希望能夠依次調用searchFiles(),readFiles()和processFiles(),並且能夠隨時訪問頁面var。 – Adam

回答

0

您將所有回調分解爲函數聲明而不是內聯表達式,因此已經是+1,因爲您可以導出和測試函數對象。

對於這個迴應,我假設優先級是獨立的進步unittests,沒有嘲諷require。 (當我進入一個新項目時,我通常發現自己正在重構遺留的node.js)。

當我走下來的嵌套嵌套回調的這條路線,我認爲是最簡單的方式一起工作是匿名錶達式的嵌套鏈回調:(在僞代碼)

function doFiles() { 
    glob('something', function(files) { 
     for (f in files) { 
     fs.readFile(file, function(err, data) { 
      processFile(data); 
     } 

     } 
    } 
} 

測試上面的程序是相當令人費解的。做到這一點的唯一方法是嘲笑需求。爲了測試這個readFile回調是否正常,你必須在它之前控制所有的通話!這在測試中違反了隔離。

第二個最好的方法,即imo,就像你所做的那樣打出回調。

它允許單元測試更好的隔離,但仍需要嘲諷需要fsglob

第三個最好的方法,即imo,注入所有的函數依賴關係,允許輕鬆配置模擬對象。這通常看起來很奇怪,但對我來說,目標是100%的覆蓋率,在孤立的單元測試中不使用模擬需求庫。它使得每個函數都是一個易於測試的獨立對象,併爲其配置模擬對象,但通常會使該函數調用更復雜!

要做到這一點:

function searchFiles() { 
    glob("*.md", { cwd: SEARCHPATH }, readFiles); 
} 

將成爲

function searchFiles (getFiles, getFilesCallback) { 
    getFiles("*.md", { cwd: SEARCHPATH }, getFilesCallback); 
} 

然後,它可以與

searchFiles(glob, readFiles) 

被稱爲這看起來有點古怪,因爲它是一個線功能,但說明了如何將依賴關係注入到函數中,以便您的測試可以配置模擬對象並將它們直接傳遞給函數。重構readFiles做到這一點:

function readFiles (err, files, readFile, processFileCb) { 
    if(err) throw err; 

    for (var file of files) { 
     readFile(SEARCHPATH + file, 'utf8', processFileCb); 
    } 
} 

readFiles發生在一個readFile方法(fs.readFile和回調,一旦文件被讀取執行哪個允許在程序測試的模擬對象的簡單的配置

然後測試可能。在僞代碼:

it('throws err when error is found', function() { 
    var error = true; 
    assert throws readFiles(error) 
}); 

it('calls readFile for every file in files', function() { 
    var files = ['file1']; 
    var error = false; 
    var readFile = createSpyMaybeSinon?(); 
    var spyCallback = createSpy(); 
    readFiles(error, files, readFile, spyCallback); 

    assert(readFile.calls.count(), files.length) 
    assert readFile called with searchpath + file1, 'utf8', spyCallback 
}); 

一旦你有需要客戶提供所有的功能依賴這些功能,那麼他們需要的創意舞蹈ing回調函數或小函數表達式來包裝調用。

上述假設完整的測試覆蓋的無嘲諷的endgoal需要,這可能不是你的目標:)


「更清潔」的方式海事組織只是使用承諾從beginnning,這是一個異步調用的精彩抽象。

+0

謝謝dm - 我的動機不是測試,而是優雅的解決方案。我第一次嘗試的確是嵌套嵌套回調,正如你第一次指出的那樣。嚴重的眼睛和大腦。回調的菊花鏈似乎沒有足夠的關注點分離。 – Adam

+0

@Adam imo,工作的代碼比優雅的代碼更重要,但優雅的工作代碼非常棒:) :)完全基於觀點,「最優雅」將是基於承諾的解決方案 – dm03514

+0

非常真實。感謝您花時間發佈重構示例 - 有助於思考。 – Adam