如何同步ajax和非ajax代码路径

时间:2014-09-11 14:43:17

标签: javascript jquery ajax promise

所以我有这个篮子功能,你输入一个作者的名字,然后列出可用的书籍。您可以选择所需内容,然后单击以选择其他作者。当你这样做时,你得到的列表大致如下:

Stephen King                                 
       The Stand                             [remove]
       The Talisman                          [remove]
       Pet Sematary                          [remove]

Terry Pratchett
       Mort                                  [remove]
       Guards Guards                         [remove]

在上面的例子中,斯蒂芬·金的书籍已经存放在Session中,而Terry Pratchett的书籍却没有。如果我单击Pratchett书上的删除按钮,一些jquery将隐藏这些。如果我删除了Stephen King的书,那么在jquery隐藏它之前会触发ajax请求将其从Session中删除。

所以我的javascript看起来像这样:

$('.book-remove').click(removeBook);

function deleteFromBasket(bookId) {
    var ajaxArgs = { BookId : bookId };
    return $.ajax({
        // blah blah, no problems here
        success: function(e) {
            hideRow();
        }
    });
}

function hideRow(bookId) {
        $('.book-id-' + bookId).hide();
}

function removeBook(e) {
    e.preventDefault();

    if ($(this).hasClass('needs-ajax-call') {
        var promise = deleteFromBasket($(this).prop("id").replace("book-id-", ""));

        // this is the problem.  how do I wait here for the ajax to complete?
    }
    else
        hideRow();

    // if i put promise.done(hideRow) up there, it still runs this too soon.
    doMoreStuff();
}

3 个答案:

答案 0 :(得分:2)

您可以构建所有代码(使用和不使用ajax的代码路径)来使用promises。不需要执行ajax调用的代码可以从已经解析的promise开始,并且两个代码路径将执行相同的序列(一个将更快执行,因为它不必等待对于ajax调用),但两者都将以相同的顺序执行:

你想要在ajax调用之后完成的任何事情只需要移动到promise.then()处理程序中:

function removeBook(e) {
    e.preventDefault();
    var promise;

    if ($(this).hasClass('needs-ajax-call') {
        promise = deleteFromBasket($(this).prop("id").replace("book-id-", ""));
    }  else {
        // get an already resolved promise since no ajax call is required
        promise = $.Deferred().resolve();
    }
    // you can return this promise if you want the calling code 
    // to be able to know when you're done with everything
    return promise.then(function() {
        hideRow(bookId);    // assumes you've calculated a bookId somewhere
        doOtherStuff();
    });
}

这样做的好处是,你的代码的lions共享位于一个代码路径而不是两个独立的代码路径中,它解决了你的问题,因为.then()处理程序中的所有代码在ajax调用之后才会执行完了。

答案 1 :(得分:1)

这是问题的答案以及一些更整洁的DOM / javascript的建议。

首先,让我们确保:

  • “会话”条目位于静态容器中(例如<div>class="sessionResultsWrapper"
  • “非会话”条目位于静态容器中(例如<div>class="otherResultsWrapper"
  • 每个条目都是<li>class="entry"
  • 的元素(例如data-bookID="xxxx"

现在你处于更好的位置:

  • 选择元素而无需繁琐的id解析
  • 建立一个点击处理程序,它将触发所有现有的“删除”按钮以及稍后添加到静态容器的任何按钮。
$(".sessionResultsWrapper, .otherResultsWrapper").on('click', '.book-remove', function(e) {
    e.preventDefault();
    var $this = $(this),
        promise;
    if ($this.parents(".sessionResultsWrapper").length) {
        promise = deleteFromBasket($this.closest('entry').data('bookID'));
    } else {
        promise = $.when();//this is the most compact syntax for a resolved promise
    });

    // At this point, you have a promise regardless of whether a "session" or "non-session" button was clicked.
    // The only difference is that a "non-session" promise will be already resolved, while a "session" promise will be resolved later.
    // Because of the way promises work, we can simply chain a .then(fn), which will fire immediately (non session, Terry Pratchett title) or later (when ajax successfully returns, Stephen King title).

    promise.then(function() {
        $this.closest(".entry").hide();//or, if the entry will never be re-shown then .remove()
        doMoreStuff();
    });
});

答案 2 :(得分:0)

您可以将doMoreStuff添加为promise的回调。 像这样的东西

function removeBook(e) {
    e.preventDefault();

    if ($(this).hasClass('needs-ajax-call') {
        var promise = deleteFromBasket($(this).prop("id").replace("book-id-", ""));
        promise.done(doMoreStuff);
    }
    else{
        hideRow();
        doMoreStuff();
    }
}