顺序,从属延迟呼叫

时间:2014-05-27 20:56:36

标签: javascript jquery ajax deferred

我有两个函数 getStudentById(studentId) getBookTitleById(bookId),其中数据通过ajax调用检索(这些函数将保持不变)

我的目标,使用Deferreds:

  1. 获取Student对象,
  2. 然后根据学生对象的 favoriteBookId 属性获取图书标题,
  3. 然后调用方法 showMessage(student,bookTitle),结果" John喜欢Book 222"
  4. 此示例已简化。本质上,目标是连续进行一定数量的ajax调用,其中下一个调用取决于前一个调用的结果,最后提供对所有结果的访问权限

    代码:

    var getStudentById = function(studentId){
      return $.Deferred(function(def){
          def.resolve({name:"John",favoriteBookId:222});  //assume ajax gets Student obj
        }).promise();  
    }
    
    var getBookTitleById = function(bookId){
      return $.Deferred(function(def){
          def.resolve("Book " + bookId);  //assume ajax call gets title
        }).promise();   
    }
    
    var showMessage = function(student, bookTitle){
       alert(student.name + " likes " + bookTitle)
    } 
    

    我见过许多遵循以下模式的解决方案

    deferred_a.then(b).then(c).then(y).then(z).done(r)
    

    将转换为:

    getStudentById(5)    //resolves with student object
    .then(function(student){
        return getBookTitleById(student.favoriteBookId)}  
    )   
    .done(function(bookTitle){
        alert(bookTitle);  //obviously  this works
    
        // how can I get reference to the student object here? so i can say:
        // showMessage (student, bookTitle)
    });
    

    但我需要调用方法 showMessage(student,bookTitle),其中参数是链中每个ajax调用的结果

    我找不到一个(优雅)示例,其中所有顺序延迟调用的结果都可以在链的末尾访问,因为 done 函数获取最后的结果然后

    我最好的解决方案是在工厂类型方法中包装第二个Deferred,但这不是最佳解决方案,对吧?我是否遗漏了延期用法中应该简化的内容?

    getStudentById(5)    
    .then(function(student){
            return $.Deferred(function(def){
                getBookTitleById(student.favoriteBookId)  
                .done(function(bookTitle){def.resolve({student:student, bookTitle:bookTitle})})
            }).promise();   
        })  
    .done(function(studentAndBookTitle){
        alert(studentAndBookTitle.student.name + " likes " + studentAndBookTitle.bookTitle);
    });
    

2 个答案:

答案 0 :(得分:0)

将promises链接在一起的另一种方法是让一个then处理程序返回另一个promise。这样做的好处是你可以利用闭包来引用第二个promise的解析中第一个promise的解析中的变量。

所以你的例子看起来像:

getStudentById(5)
.then(function(student) {
    return getBookTitleById(student.favoriteBookId)
    .then(function (bookTitle) {
        doSomethingWithStudentAndBookTitle(student, bookTitle)
    });
});

function doSomethingWithStudentAndBookTitle(student, bookTitle) {
    alert(student.name + " likes " + bookTitle);
}

编辑:此解决方案与您的上一个代码段有些相似;但

  1. getBookTitleById已经返回一个承诺,您不需要将其包裹在延期中,然后从中获得承诺。

  2. 您已经使用闭包将这两个属性包装到一个对象中;没有理由(至少没有我能看到的)你需要将这些属性包起来,然后在不同的地方对它们做些什么,而不是仅仅在那里做些什么。

    < / LI>

    编辑2:创建了一个将数据消耗与数据提供分开的功能。

答案 1 :(得分:0)

Genob,您的最佳解决方案&#34;是完全可行和可接受的。它只需要整理:

getStudentById(5).then(function(student) {
    return getBookTitleById(student.favoriteBookId).then(function(bookTitle) {
        return {
            student: student,
            bookTitle: bookTitle
        });
}).done(function(superObj) {
    alert(superObj.student.name + " likes " + superObj.bookTitle);
});

您可能也会考虑相同方法的温和变体,这是可能的,因为student已经是一个对象,允许它被扩展而不是创建一个超级对象。

getStudentById(5).then(function(student) {
    return getBookTitleById(student.favoriteBookId).then(function(bookTitle) {
        return $.extend(student, {
            favoriteBookTitle: bookTitle
        });
    });
}).done(function(extendedStudent) {
    alert(extendedStudent.name + " likes " + extendedStudent.favoriteBookTitle);
});

这种方法(两种变体)可以概括为&#34;在累加器对象中沿着承诺链传递数据&#34;或者更简单地说是承诺的数据累加器&#34;模式(我的术语 - 你不会在其他任何地方找到它。)

无论如何实现,保持&#34;消费者&#34;方法(终端.done())不受嵌套/闭包的限制。