有没有一种方法可以编写JavaScript,使其在处理许多回调时更具可读性

Is there a way to write JavaScript so it is more readable when dealing with many callbacks?

本文关键字:处理 许多 回调 可读性 JavaScript 一种 方法 有没有      更新时间:2023-09-26

我使用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) {

与其创建一个内联函数(不可重用(,不如问问自己,我想对一个县列表做什么,并独立构建它。

// write a nice comment here to explain what the function does and what the arguments mean
function handleCountyList(countries, success){
}

现在您可以按如下方式调用ListCounties

ListCounties(handleCountyList);

通过将所有功能拆分为更易于管理的部分,您将避免出现梯形图。

这也应该使您的代码更易于维护,因为它变得更易于阅读、思考、调试和更新。同情其他开发人员,并问:"如果我向我的朋友展示了这段代码,她能很容易地理解它的作用吗?">


如果你想更高级一点,而回调是让你讨厌的,那就试试承诺。在我看来,它们在语义和功能上都更好,并允许您执行以下操作:

ListCounties().then(function(counties){
    // do something with the counties
});

承诺有不同的味道,但这个图书馆是一个良好的开端:https://github.com/petkaantonov/bluebird

命名回调并分离逻辑,例如

ListCounties(function(counties,success) {
                if (success) {....

变为:

ListCounties(processCounties);
function processCounties(counties,success)   
   if (success) {...
}

查看您的代码,您可以做几件事,但在此之前,在整个代码库中保持一致性要重要得多-由于大多数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
     ...

对于保护子句来说,这不是一个好地方(保护块实际上有程序逻辑,为wasps执行的第二个块更像是一个失败块,就像交换机情况下的失败块一样(

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/


委派的事件处理程序

我看到你有一个地方可以做(当你刚刚添加了一些.郡结果项元素时(

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/(参见委托事件部分(


其他答案中已经列出了其他方法。