如何在Parse Promise链中传递额外的数据

15
在我的 Parse Cloud 代码中,我需要运行几个连续的查询,每个查询都使用“find()”方法。
例如:
var promise = firstQuery.get(objectId).then(function(result1){
            return secondQuery.find();
        }).then(function(result2){
            return thirdQuery.find();
        }).then(function(result3) {

             // here I want to use "result1", "result2" and "result3"
        });
问题是:在最终的“then”语句中,如何访问“result1”和“result2”,而不将它们分配给在父范围内声明的变量。为什么要这样做:如果您嵌套了一堆承诺,并在循环中创建它们以便并行执行(想象一下在上面的语句周围有一个for循环,其中所有承诺都被放置在数组中,然后使用“Parse.Promise.when”进行评估。它们会同时开始修改父范围变量。)我应该创建某种承诺对象,在其中返回类似以下内容的东西:
Parse.promise({result:result1,findResult:secondQuery.find()};

我可以通过执行以下操作从“result2”参数中获取值

result2.result 

result2.findResult

我希望我表达清楚了。这并不是很容易解释。


你是否在第二个查询中使用了第一个查询的结果?或者你可以并行运行所有三个查询,最后只需检查所有三个查询的结果吗?如果是这样,那么有一种更快、更简单的方法来完成这个任务。 - jfriend00
所以你把我的问题标记为“曾经被问过”,Bergi,然而我的问题是来自2014年8月,而你的是来自2015年1月? - Joris Mans
@JorisMans:我已将其标记为重复,这是完全有效的,因为另一个问题是关于此主题的权威问题(不确定你所说的“技术上它们不正确”是什么意思)。我必须承认,在创建规范问题时,我并没有看到你的问题及其相当好的答案;我只是在使用搜索时发现了你的问题。请注意,“之前提问”是一个已知的错误,并不意味着日期在关闭决定中有任何影响。 - Bergi
3个回答

25

您可以利用闭包来实现此操作,而无需任何额外的对象或包装。

var promise = firstQuery.get(objectId).then(function(result1){
    return secondQuery.find()
    .then(function(result2) {
        return thirdQuery.find()
        .then(function(result3) {
            //can use result1, result2, result3 here
        });
    });
});

这种“嵌套”语法在功能上与您的“链接”语法相同。


基于评论的编辑

如果您的 Promise 链足够长且复杂,以至于此嵌套语法变得笨重,那么逻辑可能足够复杂,值得抽象为自己的函数。

function complexQuery(objectId) {
    var results = {};
    return firstQuery.get(objectId).then(function(result1) {
        results.result1 = result1;
        return secondQuery.find();
    })
    .then(function(result2) {
        results.result2 = result2;
        return thirdQuery.find();
    })
    .then(function(result3) {
        results.result3 = result3;
        return results;
    });
}

complexQuery(objectId)
.then(function (results) {
    //can use results.result1, results.result2, results.result3
});

就我个人而言,我认为这比折腾 .bind 来说更容易阅读和维护。


“毁灭金字塔”(The Pyramid of Doom) - Madara's Ghost
1
我知道这个方法是可行的,但是当你在嵌套中变得过于疯狂时,你最终会失去 Promise 的一个重要优势,即如果你有连续 10 次或更多次调用,你不需要将它们无限嵌套。 - Joris Mans
@SecondRikudo,这并不像你想的那样适用,除非“高度嵌套的代码很丑陋和令人困惑”。你不能通过显式定义命名回调函数来简单解决这个问题。 - Retsam
一个可能的情况是,您正在查找10个不同表中的对象以删除它们,并且只有在一路上没有错误时才删除所有对象。由于Parse不支持事务,如果您在删除对象时遇到错误,就会出现各种问题。 - Joris Mans
我简直想不通链式 Promise 直到看了这个答案。解决了我的烦恼,谢谢! - NobleUplift
显示剩余2条评论

13

你不能使用父级作用域的技巧

好吧,既然其他回答都这样做了,让我提出一个不那样做的解决方案。您可以沿着已解决的承诺传递。这也有额外的好处,即没有嵌套或闭包。

这基于承诺已经是值的代理的概念,因此您实际上不需要创建一个长链:

var firstObject = firstQuery.get(objectId);
var secondObject = firstObject.then(secondQuery.find.bind(secondQuery));
var thirdObject = secondObject.then(thirdQuery.find.bind(thirdQuery));
Promise.all(firstObject, secondObject, thirdObject).then(function(r1, r2, r3){
   // here you can use "r1", "r2" and "r3"
});

使用标准的Promise,代码看起来会像这样:

Promise.all([firstObject, secondObject, thirdObject]).then(function(){
   var r1 = arguments[0], r2 = arguments[1], r3 = arguments[2]; 
   // here you can use "r1", "r2" and "r3"
});
使用bluebird库,您可以使用.spread 来进行快捷方式或绑定实际上下文。如果您一定要创建一个链,请使用Promise.all返回多个promise以传递上下文,但我认为这种方法更可取。

这在 Parse 云代码中可以工作吗? - Joris Mans
否则我不会以这种方式发布它。 - Benjamin Gruenbaum

1
如果您想保持扁平化的链式语法而不是嵌套,可以使用来自父作用域的对象进行共享:
var shared = {};
var promise = firstQuery.get(objectId).then(function(result1){
    // save results to shared object
    shared.result1 = result1;
    return secondQuery.find();
}).then(function(result2){
    shared.result2 = result2;
    return thirdQuery.find();
}).then(function(result3) {
    // here I want to use "result1", "result2" and "result3"
    // just use shared.result1, shared.result2
});

如问题所述,我正在寻找一种不涉及父级作用域的解决方案。 - Joris Mans
你需要澄清关于父级作用域的问题,如果“它们同时修改父级作用域”真的是个问题,那么所有共享访问都可能成为问题。同时请注意,Promise并不意味着多线程,您仍然只能一次执行一个,Parse.Promise.when()将在链接到成功处理程序之前简单地处理它们。 - Timothy Walters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接