有没有办法写 JavaScript 以便在处理许多回调时更具可读性?
Is there a way to write JavaScript so it is more readable when dealing with many callbacks?
我在 JavaScript 工作。由于代码的外观,我越来越沮丧。代码如此嵌套,我很快就需要投资第三台 37" 显示器,以防止一直损坏我的手指。
这是我现在正在处理的一些工作代码:
$(function () {
var mainContainer = $('#mainContainer'),
countyContainer = $('#countyContainer'),
workAreaContainer = $('#workAreaContainer');
$('body').fadeIn(400, function() {
mainContainer.slideDown(400, function() {
ShowLoader();
ListCounties(function(counties,success) {
if (success) {
HideLoader();
for (var i = 0; i < counties.length; i++) {
if (counties[i] != "") {
countyContainer.append(
'<div class="col-md-3 county-result-item">'+
'<h3>'+counties[i]+'</h3>'+
'<i class=" '+FA('fa-folder-open','3x')+' text-center" style="color:'+RandomColor()+'"/>'+
'</div>'
);
}
}
var countyResultItem = $('.county-result-item');
countyResultItem.on('click', function(event) {
event.preventDefault();
var county = $(this).text().split("(")[0];
if (county != "") {
ShowLoader();
countyContainer.slideUp(400);
FetchWorkAreas(county,function(workAreaData,success) {
if (success) {
for (var i = 0; i < workAreaData.workAreas.length; i++) {
workAreaContainer.append(
'<div class="col-md-3 workArea-result-item">'+
'<h3>'+workAreaData.workAreas[i]+'</h3>'+
'<i class=" '+FA('fa-folder-open','3x')+' text-center" style="color:'+RandomColor()+'"/>'+
'</div>'
);
}
HideLoader();
workAreaContainer.slideDown(400, function() {
var workAreaResultItem = $('.workArea-result-item');
workAreaResultItem.on('click', function(event) {
event.preventDefault();
var selectedWorkArea = $(this).text().split("(")[0];
FetchJobListings(workAreaData.countyID,selectedWorkArea,function(jobListings,success) {
if (success) {
console.log(jobListings);
}
});
});
})
}
});
}
});
}
});
});
});
function FetchJobListings(countyID,selectedWorkArea,callback) {
$.post('Fetch.php', {fetch: 'jobListings',countyID : countyID, selectedWorkArea: selectedWorkArea}, function(data) {
if (data) {
callback(data,true);
}
});
}
function FetchWorkAreas(county,callback)
{
$.post('Fetch.php', {fetch: 'workAreasID',county:county}, function(data) {
if (data && data.workAreas.length > 0) {
callback(data,true);
}
});
}
function ListCounties(callback)
{
$.post('Fetch.php', {fetch: 'counties'}, function(data) {
if (data) {
if (data.length > 0) {
callback(data,true);
}
}
});
}
function RandomColor() {
var colors = ["#cea547","#7e8b58","#002c44","6da6a7"];
var rand = Math.floor(Math.random()*colors.length);
return colors[rand];
}
function FA(icon,size) {
var iconSize = '';
if (typeof size != undefined) {
iconSize = size;
}
return 'fa '+ icon+' fa-'+size;
}
function ShowLoader() {
if ($('.imgLoader').length === 0) {
var loaders = ["loading1.gif","loading2.gif","loading3.gif","loading4.gif"];
var rand = Math.floor(Math.random()*loaders.length);
$('#mainContainer').append('<div class="imgLoader"><img class="imgLoader img-responsive img-center" src="imgs/'+loaders[rand]+'" /><h3 class="text-center">Laster</3></div>');
}
}
function HideLoader() {
$('.imgLoader').fadeOut(400,function() {
$(this).remove();
});
}
});
这让我的眼睛受伤了,我真的有点难过,因为对我来说,代码可读性和美观性很重要,而且不要最终看起来像鱼梯:
}
});
});
})
}
});
}
});
}
});
});
});
在JavaScript这方面我还是我所谓的菜鸟,这可能是我没有注意到的根本问题。但基本上,有没有办法保留代码,以便在我完成它时它不会缩进 1000 次?
为您的回调命名并分隔逻辑,例如
ListCounties(function(counties,success) {
if (success) {....
变成:
ListCounties(processCounties);
function processCounties(counties,success)
if (success) {...
}
这是边界线的例子 Spaghetti Code。
您可以做几件事,首先是拆分所有功能,以便它们作为较小的进程独立存在。例如:
ListCounties(function(counties,success) {
if (success) {
与其创建内联函数(不可重用),不如问问自己我想对县列表做什么,然后独立构建它。
// write a nice comment here to explain what the function does and what the arguments mean
function handleCountyList(countries, success){
}
现在您可以按如下方式调用您的 ListCounties
:
ListCounties(handleCountyList);
通过将所有功能拆分成更易于管理的部分,您将避免梯子。
这还应该使您的代码更易于维护,因为它变得更易于阅读、思考、调试和更新。换位思考其他开发者,问问,"If I showed my friend this code, could she easily understand what it does?"
如果您想更高级一点,而回调又让您烦恼,请尝试使用 promises。在我看来,它们在语义上和功能上都更好,并且允许您执行以下操作:
ListCounties().then(function(counties){
// do something with the counties
});
Promise 有不同的风格,但这个库是一个好的开始:https://github.com/petkaantonov/bluebird
查看您的代码,您可以做几件事,但在此之前,在您的代码库中保持一致更为重要 - 大多数 IDE 允许您崩溃块处理编码风格的转变比深度嵌套更难
保护条款
你可以更换你的
ListCounties(function(counties,success) {
if (success) {
...
和
ListCounties(function(counties,success) {
if (!success)
return;
...
减少 1 层嵌套。在决定继续处理大部分代码之前,您可以将此应用于您所做的大部分检查。
请注意,如果保护条件不是很明显,这会降低代码的可读性。例如,虽然这是一个保护条款的好地方(保护条款更像是一个过滤器)
KillWasps(function(bug) {
if (bug !== "wasp")
return
// code to deploy nuclear warheads
...
这 NOT 是一个保护子句的好地方(保护块实际上有程序逻辑和执行 wasps
的第二个块更像是一个秋天-through 块,就像 switch case 的 fall-through 块一样)
HandleInsect(function(bug) {
if (bug !== "wasp") {
// code to sign up bug for insects anonymous
return
}
// code to deploy nuclear warheads
...
在这种情况下我会使用 else
方块(和厚厚的弹匣)。
相关阅读 : http://blog.codinghorror.com/flattening-arrow-code/
委派的事件处理程序
我看到你有一个地方可以做(当你刚刚添加了一些 .county-result-item 元素时)
var countyResultItem = $('.county-result-item');
countyResultItem.on('click', function (event) {
...
您可能需要考虑将其移至委派的事件处理程序
countyContainer.on('click', '.county-result-item', function(event) {
...
并且您可以将其从执行 append
的当前处理程序内部移动到外部范围。
当然,如果您的 DOM 中没有任何 .county-result-item
,这是一个很好的方法来迷惑那些无法轻易弄清楚 .county-result-item
在哪里的人/ 被添加 - 这将是 BAD,因此如果是这种情况,请保持您的 append
逻辑关闭您的委托点击处理程序。
相关阅读:http://api.jquery.com/on/(参见委托事件部分)
其他答案中已经列出了其他方法。
我在 JavaScript 工作。由于代码的外观,我越来越沮丧。代码如此嵌套,我很快就需要投资第三台 37" 显示器,以防止一直损坏我的手指。
这是我现在正在处理的一些工作代码:
$(function () {
var mainContainer = $('#mainContainer'),
countyContainer = $('#countyContainer'),
workAreaContainer = $('#workAreaContainer');
$('body').fadeIn(400, function() {
mainContainer.slideDown(400, function() {
ShowLoader();
ListCounties(function(counties,success) {
if (success) {
HideLoader();
for (var i = 0; i < counties.length; i++) {
if (counties[i] != "") {
countyContainer.append(
'<div class="col-md-3 county-result-item">'+
'<h3>'+counties[i]+'</h3>'+
'<i class=" '+FA('fa-folder-open','3x')+' text-center" style="color:'+RandomColor()+'"/>'+
'</div>'
);
}
}
var countyResultItem = $('.county-result-item');
countyResultItem.on('click', function(event) {
event.preventDefault();
var county = $(this).text().split("(")[0];
if (county != "") {
ShowLoader();
countyContainer.slideUp(400);
FetchWorkAreas(county,function(workAreaData,success) {
if (success) {
for (var i = 0; i < workAreaData.workAreas.length; i++) {
workAreaContainer.append(
'<div class="col-md-3 workArea-result-item">'+
'<h3>'+workAreaData.workAreas[i]+'</h3>'+
'<i class=" '+FA('fa-folder-open','3x')+' text-center" style="color:'+RandomColor()+'"/>'+
'</div>'
);
}
HideLoader();
workAreaContainer.slideDown(400, function() {
var workAreaResultItem = $('.workArea-result-item');
workAreaResultItem.on('click', function(event) {
event.preventDefault();
var selectedWorkArea = $(this).text().split("(")[0];
FetchJobListings(workAreaData.countyID,selectedWorkArea,function(jobListings,success) {
if (success) {
console.log(jobListings);
}
});
});
})
}
});
}
});
}
});
});
});
function FetchJobListings(countyID,selectedWorkArea,callback) {
$.post('Fetch.php', {fetch: 'jobListings',countyID : countyID, selectedWorkArea: selectedWorkArea}, function(data) {
if (data) {
callback(data,true);
}
});
}
function FetchWorkAreas(county,callback)
{
$.post('Fetch.php', {fetch: 'workAreasID',county:county}, function(data) {
if (data && data.workAreas.length > 0) {
callback(data,true);
}
});
}
function ListCounties(callback)
{
$.post('Fetch.php', {fetch: 'counties'}, function(data) {
if (data) {
if (data.length > 0) {
callback(data,true);
}
}
});
}
function RandomColor() {
var colors = ["#cea547","#7e8b58","#002c44","6da6a7"];
var rand = Math.floor(Math.random()*colors.length);
return colors[rand];
}
function FA(icon,size) {
var iconSize = '';
if (typeof size != undefined) {
iconSize = size;
}
return 'fa '+ icon+' fa-'+size;
}
function ShowLoader() {
if ($('.imgLoader').length === 0) {
var loaders = ["loading1.gif","loading2.gif","loading3.gif","loading4.gif"];
var rand = Math.floor(Math.random()*loaders.length);
$('#mainContainer').append('<div class="imgLoader"><img class="imgLoader img-responsive img-center" src="imgs/'+loaders[rand]+'" /><h3 class="text-center">Laster</3></div>');
}
}
function HideLoader() {
$('.imgLoader').fadeOut(400,function() {
$(this).remove();
});
}
});
这让我的眼睛受伤了,我真的有点难过,因为对我来说,代码可读性和美观性很重要,而且不要最终看起来像鱼梯:
}
});
});
})
}
});
}
});
}
});
});
});
在JavaScript这方面我还是我所谓的菜鸟,这可能是我没有注意到的根本问题。但基本上,有没有办法保留代码,以便在我完成它时它不会缩进 1000 次?
为您的回调命名并分隔逻辑,例如
ListCounties(function(counties,success) {
if (success) {....
变成:
ListCounties(processCounties);
function processCounties(counties,success)
if (success) {...
}
这是边界线的例子 Spaghetti Code。
您可以做几件事,首先是拆分所有功能,以便它们作为较小的进程独立存在。例如:
ListCounties(function(counties,success) {
if (success) {
与其创建内联函数(不可重用),不如问问自己我想对县列表做什么,然后独立构建它。
// write a nice comment here to explain what the function does and what the arguments mean
function handleCountyList(countries, success){
}
现在您可以按如下方式调用您的 ListCounties
:
ListCounties(handleCountyList);
通过将所有功能拆分成更易于管理的部分,您将避免梯子。
这还应该使您的代码更易于维护,因为它变得更易于阅读、思考、调试和更新。换位思考其他开发者,问问,"If I showed my friend this code, could she easily understand what it does?"
如果您想更高级一点,而回调又让您烦恼,请尝试使用 promises。在我看来,它们在语义上和功能上都更好,并且允许您执行以下操作:
ListCounties().then(function(counties){
// do something with the counties
});
Promise 有不同的风格,但这个库是一个好的开始:https://github.com/petkaantonov/bluebird
查看您的代码,您可以做几件事,但在此之前,在您的代码库中保持一致更为重要 - 大多数 IDE 允许您崩溃块处理编码风格的转变比深度嵌套更难
保护条款
你可以更换你的
ListCounties(function(counties,success) {
if (success) {
...
和
ListCounties(function(counties,success) {
if (!success)
return;
...
减少 1 层嵌套。在决定继续处理大部分代码之前,您可以将此应用于您所做的大部分检查。
请注意,如果保护条件不是很明显,这会降低代码的可读性。例如,虽然这是一个保护条款的好地方(保护条款更像是一个过滤器)
KillWasps(function(bug) {
if (bug !== "wasp")
return
// code to deploy nuclear warheads
...
这 NOT 是一个保护子句的好地方(保护块实际上有程序逻辑和执行 wasps
的第二个块更像是一个秋天-through 块,就像 switch case 的 fall-through 块一样)
HandleInsect(function(bug) {
if (bug !== "wasp") {
// code to sign up bug for insects anonymous
return
}
// code to deploy nuclear warheads
...
在这种情况下我会使用 else
方块(和厚厚的弹匣)。
相关阅读 : http://blog.codinghorror.com/flattening-arrow-code/
委派的事件处理程序
我看到你有一个地方可以做(当你刚刚添加了一些 .county-result-item 元素时)
var countyResultItem = $('.county-result-item');
countyResultItem.on('click', function (event) {
...
您可能需要考虑将其移至委派的事件处理程序
countyContainer.on('click', '.county-result-item', function(event) {
...
并且您可以将其从执行 append
的当前处理程序内部移动到外部范围。
当然,如果您的 DOM 中没有任何 .county-result-item
,这是一个很好的方法来迷惑那些无法轻易弄清楚 .county-result-item
在哪里的人/ 被添加 - 这将是 BAD,因此如果是这种情况,请保持您的 append
逻辑关闭您的委托点击处理程序。
相关阅读:http://api.jquery.com/on/(参见委托事件部分)
其他答案中已经列出了其他方法。