如何在返回函数变量之前等待 Promise 完成?

218

我仍在努力理解 Promise,但由于这里社区的帮助,我已经有了一些进展。

我有一个简单的 JS 函数用来查询 Parse 数据库。它应该返回结果数组,但由于查询的异步性质(因此需要使用 Promise),函数会在结果之前返回,导致我得到一个未定义的数组。

我需要做什么才能让这个函数等待 Promise 的结果呢?

以下是我的代码:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    var promise = query.find({
               success: function(results) {
               // results is an array of Parse.Object.
                             console.log(results);
                             //resultsArray = results;
                             return results;
               },

               error: function(error) {
               // error is an instance of Parse.Error.
                             console.log("Error");
               }
    });                           

}

3
你可以考虑使用async/await。自Node 7.6版本以来,Node已经原生支持async/await。 - Viliam Simko
5个回答

85

不要返回resultsArray,而是返回一个承诺来获取结果数组,然后在调用方使用then - 这样做的好处是调用者知道函数正在执行异步I/O操作。在JavaScript中编写并发程序就是基于此- 您可能需要阅读这个问题以获得更广泛的想法:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    return query.find({});                           

}

// later
resultsByName("Some Name").then(function(results){
    // access results here by chaining to the returned promise
});

您可以在Parse的博客文章中查看使用解析承诺与查询的更多示例。


你能告诉我这个的支持情况吗?IE9支持这个吗? - sandrina-p
4
啊,所以这不仅仅是纯 JavaScript?我正在寻找一种只使用纯 JavaScript 的方法来实现这一点(等待一个函数完成后再开始另一个函数)。 - sandrina-p
50
我已经阅读了SO两天了,但是仍然没有人解决这个问题。这个被接受的答案和其他所有答案一样。函数返回一个Promise,而不是OP所要求的值。为什么这个答案被标记为被接受的呢? - iGanja
4
我已经阅读了3分钟的SO,但仍然感到烦恼,因为人们不能理解使用情况是不同的,如果我想要做一些同步的东西,并不意味着我是一个不知道自己需要什么的初学者。 - Alexandre G
1
没问题,有82个人发现这个答案很有用,如果你想使用其中一种导致性能非常差的强制同步方式来发布替代答案,随意发表。如果人们觉得有用,他们会点赞的。 - Benjamin Gruenbaum
显示剩余3条评论

40

我需要做什么才能让这个函数等待 Promise 的结果?

使用 async/await(不是 ECMA6 的一部分,但自 2017 年底起可在 Chrome、Edge、Firefox 和 Safari 中使用,参见 canIuse
MDN

    async function waitForPromise() {
        // let result = await any Promise, like:
        let result = await Promise.resolve('this is a sample promise');
    }

由于评论而添加: 异步函数始终返回一个Promise,在TypeScript中,它看起来像:

    async function waitForPromise() {
        // let result = await any Promise, like:
        let result: Promise<string> = await Promise.resolve('this is a sample promise');
    }

14
如果在不使用await(或在非异步代码中)调用异步函数,它仍将返回一个Promise对象。 如果不确定,请检查console.log(waitForPromise())的结果。 在异步函数内部使用console.log(result)进行检查将打印出您预期的内容,但异步函数的返回会立即发生而不会阻塞,并返回一个Promise。在JavaScript中阻塞通常是非常糟糕的,因为它是单线程应用程序,阻塞将使任何其他发布/订阅客户端无法获得通知,并且最终使整个应用程序崩溃。 - SRM
8
.NET的“任务”类有.wait()方法用于“promise”,JavaScript是否缺少此功能?在退出我的Node命令行工具之前,我需要等待某些内容,这可能会将其输出传输到另一个工具。 "await"只能在异步函数内部使用,这意味着它无法在promise的范围之外工作。 - TamusJRoyce
@SRM,我觉得你的评论基于对示例的错误解释 - 它是关于“内部”Promise.resolve(作为最简单的Promise示例),因此没有外部调用者,正如你在评论中所述。所以我决定更新答案。 - Martin Meeser
@TamusJRoyce 这可能是一个独立的问题,但我认为在C#中,您可以使用Task.ContinueWith(Task),这与接受的答案中看到的想法相同(其中称为“then()”)。 - Martin Meeser
我现在明白了。我可以将整个脚本包装在一个巨大的异步函数中,并在脚本的最后一行调用该函数。那个函数仍然有点样板文件,但比我之前感觉的要少得多。我不习惯在函数内编写函数。谢谢@MartinMeeser! - TamusJRoyce
这个函数在 ES5 中使用吗? - Merve Gül

3

在这里你并没有实际使用Promise。Parse允许你使用回调函数或Promise,由你选择。

若要使用Promise,请按照以下步骤进行:

query.find().then(function() {
    console.log("success!");
}, function() {
    console.log("error");
});

现在,要在承诺完成后执行一些操作,只需在then()调用内的承诺回调中执行即可。到目前为止,这与常规回调完全相同。

实际上,真正充分利用承诺的方式是将它们链接起来,像这样:

query.find().then(function() {
    console.log("success!");

    return new Parse.Query(Obj).get("sOmE_oBjEcT");
}, function() {
    console.log("error");
}).then(function() {
    console.log("success on second callback!");
}, function() {
    console.log("error on second callback");
});

结果对象是什么类型的?因为它似乎不包含我的数组。 - mac_55
它应该是一个 Parse.Object 数组。 - mash

2

您不希望使函数等待,因为JavaScript旨在是非阻塞的。 相反,在函数末尾返回Promise,然后调用函数可以使用Promise获取服务器响应。

var promise = query.find(); 
return promise; 

//Or return query.find(); 

2
你整个关于 success: 部分的回调都不对。 - Benjamin Gruenbaum
我尝试了这个,但结果似乎是未定义的。resultsByName("name").then(function(results){ console.log("got array "+results.count); }); - mac_55
作为测试,尝试以下操作:var promise = resultsByName("name");console.log(promise); 在控制台中,您应该会看到一个 Promise。请让我知道您的发现。另外,请在网络选项卡中检查是否实际返回任何内容。 - html_programmer
我在声明 promise 变量后直接运行了日志语句。在日志中得到了这个结果:{"_resolved":false,"_rejected":false,"_resolvedCallbacks":[],"_rejectedCallbacks":[]} - mac_55
1
谢谢,结果函数内部肯定有某种错误。现在它已经可以工作了。我将console.log更改为results.length,并且可以看到返回的数组中有1个条目 :) - mac_55
显示剩余5条评论

0
所以,结果是代码异步运行,但是主代码的调用者要启动异步事件,仍然需要查询承诺/结果,以便获得它。那么调用者是不是在阻塞(等待结果)?还是查询承诺/结果只是排队一个事件,调用者必须“等待”。也就是说,最终会触发一个事件(调用函数)来处理结果数据。

根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

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