2014-05-11 87 views
3

我在重新學習Javascript和上週的過程中編寫這些代碼爲大學任務,我認爲有可能是執行此代碼的JavaScript Node.js的回調函數,變量的作用域問題

app.get('/member/all', function(req, res) {  
    connection.query('CALL GetAllMembers()', function(err,rows){ 
     connection.query('CALL CountMembers()', function(err, allMembers){ 
      console.log(err); 
      connection.query('CALL CountAllIndMembers()', function(err,indMembers){ 
       console.log(err); 
       connection.query('CALL CountInactiveMembers()', function(err,inactiveMembers){ 
        console.log(err); 
        connection.query('CALL CountAllMembersInGroups()', function(err,groupMembers){ 
         console.log(err); 
         res.render('members', {members : rows[0], title : "All Members", groupMembers : groupMembers[0][0].AllGrpMembers, 
          inactiveMembers : inactiveMembers[0][0].AllInactiveMembers, indMembers : indMembers[0][0].AllIndMembers, 
          allMembers : allMembers[0][0].AllMembers, statistics : true}); 
         }); 
        }); 
       }); 
      }); 
     }); 
    }); 
}); 
的更好的方法時,

當我試圖在app.get下聲明變量時,例如var allMembers ...執行回調時,我無法設置allMembers = rowsFromTheCallback。它似乎是這個回調的局部變量。我相信這與變量範圍和/或提升有關。只是想問問你們,即使這個功能起作用,是否還有更好的方法來做到這一點。看哈哈是非常醜陋的!

預先感謝

傑克

+0

使他們的所有功能,而不是全部匿名呼叫! –

+0

代碼的醜陋是節點回調的不幸副作用。我會建議看看涉及生成器的東西(yield關鍵字而不是回調),但不知道是否足夠穩定以便在Node中使用。如果你想要更傳統的東西,你可以看看像async.js這樣的庫,或者使用Promises – hugomg

+0

來閱讀關於「持續傳遞樣式」的內容,這將大大增加你對此的理解。 –

回答

1

至於範圍推移,所有的內部函數應該是能夠讀取和,除非它由內變量聲明或函數參數陰影寫入到外變量。

您遇到的問題可能與代碼的異步性有關。看到這個代碼:

function delay(n, cb){ 
    setTimeout(function(){ bs(delay) }, delay); 
} 

function main(){ 
    var allMembers = 17; 
    delay(500, function(){ 
     console.log(allMembers); // This looks at the outer "allMembers" 
     allMembers = 18; 

     delay(200, function(allMembers){ // <-- SHADOW 
      console.log(allMembers); // This looks at the allMembers from "delay 200"'s callback 
      allMembers = 42; 
     }); 

     delay(300, function(){ 
      console.log(allMembers); //This is the outside "allMembers" again 
     }); 
    }); 

    return allMembers; // Still 17! 
} 

main(); 

main將返回一個定時器,甚至解僱所以它會返回該變量的原始值之前。爲了等待內部回調運行,唯一的方法是使main回調完成時signa,而不是僅僅返回。

function main(onResult){ 
    delay(500, function(){ 
     //... 
     onResult(allMembers); 
    }); 

    // <-- no return value 
}); 

main(function(allM){ 
    console.log(allM); 
}); 
+0

謝謝,已經清除了我:) – user3621357

+1

當面對需要對數據庫進行一系列調用,其中每個後續調用取決於前一次調用的返回時,此方法將導致性能不佳。更糟糕的是,如果所選的延遲時間不夠長,則會失敗。增加延遲,你只會使性能變差。 –

+0

@JasonAller:我希望延遲函數就像一些簡單的異步函數一樣,您可以使用它來測試沒有數據庫的東西並演示變量範圍。是的,我不推薦使用實際的延遲來等待數據庫響應(事實上,嵌套的數據庫調用通常是一種氣味 - 它總是值得研究,如果沒有辦法通過單個查詢獲得相同的數據) – hugomg

1

見異步庫:https://github.com/caolan/async

async.series([ 
    getAllMembers, 
    countMembers, 
    ... 
], function(err, results) { 
    // err contains an error if any of the functions fails. No more functions will be run. 
    // results is an array containing results of each function if all the functions executed without errors 
})); 

function getAllMembers(callback) { 
    connection.query('CALL CountMembers()', callback); 
} 

function countMembers(callback) { 
... 
} 

如果該函數的執行順序並不重要,async.parallel可以用來代替async.series。

0

使用庫來處理和封裝「Continuation Passing Style」(CPS)與您的異步調用進行交互的功能非常強大。下面的代碼不是來自一個庫,但是我將通過它來作爲實現CPS的一種方式的示例。

設置範圍適當的隊列是第一步。本例使用有關的最簡單的方法,這樣做的:

var nextList = []; 

之後,我們需要一種方法來處理我們的第一種情況下,在未來進行排隊任務的需要。在這種情況下,我專注於按順序執行它們,所以我將其命名爲next

function next() { 
    var todo, 
     current, 
     task, 
     args = {}; 
    if (arguments.length > 0) { // if called with parameters process them 
     // if parameters aren't in an array wrap them 
     if (!Array.isArray(arguments['0'])) { 
      todo = [arguments]; 
     } else { // we were passed an array 
      todo = []; 
      arguments['0'].forEach(function (item) { 
       // for each item we were passed add it to todo 
       todo.push(item); 
      }); 
     } 
     nextList = todo.concat(nextList); 
     // append the new items to the end of our list 
    } 
    if (nextList.length > 0) { // if there are still things to do 
     current = Array.prototype.slice.apply(nextList.shift()); 
     task = current[0]; 
     args = current.slice(1); 
     task.apply(null, args); // execute the next item in the list 
    } 
} 

這使我們能夠像電話:

.map(function (filepath) { 
    tasks.push(
     [ 
      handleAsset, 
      { 
       'path': filepath, 
      } 
     ] 
    ); 
}); 
tasks.push([done]); 
next(tasks); 

這將調用handleAsset,這是異步,每進行一次文件,爲了。這將允許你把你的代碼,並改變每個嵌套調用到一個單獨的函數形式:

function memberAll() { 
    app.get('/member/all', function(req, res) { 
     if (err) { 
      handleError(err, 'memberAll'); 
     } else { 
      next(getAllMembers, 'parameters to that call if needed'); 
     } 
    }); 
} 

其中handleError是一個常見的錯誤處理程序,並在下次調用允許您傳遞相關參數下一個需要的功能。重要的是在if語句的成功方面,你既可以:

  • 有條件地的幾個功能之一
  • 調用next與陣列通話之作,例如,如果你有功能processFolderprocessFile你可以預計,處理一個文件夾可能涉及處理其他文件夾和文件的數量會有所不同
  • 什麼都不做,除了通話next()不帶參數並結束當前分支

裝飾可以包括編寫一個乾淨的功能,用於清空nextList,將項目添加到nextList而不需要從列表中調用項目等。此時的替代方法是爲此使用現有庫或繼續編寫自己的庫。