这是使用Deferred对象的正确方法吗?

时间:2012-01-31 01:33:31

标签: javascript jquery jquery-deferred

我正在构建一个需要列出产品类别和子类别的应用程序。

当用户选择一个类别时,与该类别相关的子类别将从服务器加载ajax,但前提是它们以后未加载(在这种情况下,它们是从DOM加载的)。

代码:http://jsbin.com/abijad/edit#javascript,html

var $form = $('#new-product-form');

$form.on( 'change', 'select.product-categories', function( e ) { //I'm using event delegation for a future feature...

  getSubCategories( $(this).val() ).done( function( $subCategoriesEl ){
    $form.find( 'select.product-subcategories' ).not( $subCategoriesEl ).hide();
    $subCategoriesEl.show();
  });

});

var getSubCategories = function( categoryId ) {

    var dfd = $.Deferred(),
        $alreadyisLoaded = $form.find( 'select.product-subcategories' ).map( function( idx, el ){
      if( parseInt( $( this ).data( 'category' ), 10 ) === parseInt( categoryId, 10 ) ){
            return el;
          } 
        });

      if( $alreadyisLoaded.length > 0 ){ //don't request subCategories that already are loaded
        console.log( 'SubCategory loaded from DOM' );
        dfd.resolve( $alreadyisLoaded );
      } else {
        var subCategoriesHtml = '<select data-category="' + categoryId +  '" class="product-subcategories">';

        $.get( '/', { //The ajax request will only be simulated
          categoryId : categoryId
        }, function ( result ) {
          //simulate success :X
          result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};

          console.log( 'SubCategory loaded with Ajax' );

          if( result.status === 1 ) { //Success

            for( var subCategoryInfo in result.data) {
              if( result.data.hasOwnProperty( subCategoryInfo ) ){
                subCategoriesHtml += '<option value="' + result.data[subCategoryInfo].id + '">';
                subCategoriesHtml += result.data[subCategoryInfo].name + '</option>';
              }
            }

            subCategoriesHtml += '</select>';

            var $subCategories = $( subCategoriesHtml ).hide().appendTo( $form );

            dfd.resolve( $subCategories );
          } else {
            dfd.reject();
          }
        });
      }

      return dfd.promise();

};

<form id="new-product-form">
  <select class="product-categories">
    <option value="1">Category I</option>
    <option value="2">Category II</option>
    <option value="3">Category III</option>
    <option value="4">Category IV</option>
    <option value="5">Category V</option>
  </select>
  <select data-category="1" class="product-subcategories">
    <option value="1">SubCategory I</option>
    <option value="2">SubCategory II</option>
    <option value="3">SubCategory III</option>
    <option value="4">SubCategory IV</option>
    <option value="5">SubCategory V</option>
  </select>
</form>

因为代码在这里和那里充满了回调,我决定使用jQuery Deferred对象,但我不知道这是否是正确的实现。有人可以告诉我,我做了正确的事情,还是我应该采取不同的做法?

1 个答案:

答案 0 :(得分:1)

我没有看到任何明显错误的东西。总而言之,您正在以正确的方式使用延迟:抽象出您方法的可能双同步性质。现在,话虽这么说,如果这是代码出现在我的代码库中,这就是我写它的方式。要点是:不要在数组上使用for in,使用数组构建字符串,一致的命名和间距,以及其他一些简洁的JS首选项。这些都是品味问题,所以不然,好工作:

(function() {
    var getSubCategories = function ( categoryId ) {
        categoryId = +categoryId;

        return $.Deferred( function ( dfd ) {

            var isLoaded = form.find( 'select.product-subcategories' )
                .map( function ( index, el ) {
                    if ( +$( this ).data( 'category' ) === categoryId ) {
                        return el;
                    }
                }),
                markup = [ ];

            if ( isLoaded.length ) {
                console.log( 'SubCategory loaded from DOM' );
                dfd.resolve( isLoaded );
            } else {
                markup.push( '<select data-category="' + categoryId +  '" class="product-subcategories">' );

                var request = $.ajax({
                    url: '/',
                    data: { categoryId: categoryId }    
                });

                request.done( function ( result ) {
                    //simulate success :X
                    result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};

                    var status = result.status,
                        data = result.data,
                        i = 0,
                        il = data.length,
                        current;

                    console.log( 'SubCategory loaded with Ajax' );

                    if ( status !== 1 || !data ) {
                        dfd.reject();
                        return;
                    }

                    for ( current = data[ i ]; i < il; i++ ) {
                        markup.push( '<option value="' + current.id + '">' );
                        markup.push( current.name + '</option>' );  
                    }

                    markup.push( '</select>' );

                    dfd.resolve( $( markup.join( '' ) ).hide().appendTo( form ) );
                });
            }

        }).promise();
    };

    var form = $( '#new-product-form' )
        .on( 'change', 'select.product-categories', function ( e ) {
            getSubCategories( $( this ).val() )
                .done( function( el ) {
                    form.find( 'select.product-subcategories' )
                        .not( el )
                            .hide()

                    el.show();
                });
        });
});

作为旁注,如果ajax请求失败,只是想提出你没有任何问题的处理。在这种情况下,您需要reject,并确保编写fail方法。只是一个抬头。