从 Promise 返回一个值

Posted

技术标签:

【中文标题】从 Promise 返回一个值【英文标题】:Returning a value from a Promise 【发布时间】:2014-10-21 05:19:09 【问题描述】:

我想使用这样的 Promise 调用 Google Maps Geocoding API:

function makeGeoCodingRequest(address,bounds)

    /*
        Input parameters:
            address:a string
            bounds: an object of class google.maps.LatLngBounds(southWest,northEast)

        This will return a set of locations from the google geocoding library for the given query
     */
    var url="https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=AIzaSyD9GBloPC20X-1kWRo7sm_0z5xvCiaSd3c";
    var promise,response;
    var messages=
            "ZERO_RESULTS":"No results were found",
            "OVER_QUERY_LIMIT":"We are over the query limit.Wait awhile before making a request",
            "REQUEST_DENIED":"Request was denied,probably using a bad or expired API Key",
            "INVALID_REQUEST":"Request was sent without the required address,component or component",
            "UNKNOWN_ERROR": "There was an error somewhere on Google's servers" 
    ;
    if(address)
        promise=Q($.ajax(
            type: "GET",
            url: "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=API_KEY"
        ));
        return promise.then(function(data) 
            if (data.status === "OK") return data;
            else    console.error(messages[data.status]);
            return null;    
        );

当我调用函数makeGeoCodingRequest请求时,我发现我得到的是一个promise而不是一个值:

 var geo=makeGeoCodingRequest(address);
 console.log(Q.isPromise(geo));//returns true

为什么在返回值之前没有执行 promise.then?如何从这个 Promise 而不是另一个 Promise 中获取值?

【问题讨论】:

promise 不给出值,而是使用 .then() 将要使用该值的函数传入 Promise 【参考方案1】:

如果您依赖 Promise 来返回数据,则必须从函数中返回 Promise。

一旦您的调用堆栈中的 1 个函数是异步的,如果您想继续线性执行,所有想要调用它的函数也必须是异步的。 (异步 = 返回一个承诺)

请注意,您的if 语句没有大括号,因此如果条件失败,则只有它之后的第一条语句不会被执行。

我在这个例子中修复了它。注意我添加的备注。

if(address)
    promise=Q($.ajax(
        type: "GET",
        url: "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=API_KEY"
    ));
    return promise.then(function(data) 
        // whatever you return here will also become the resolve value of the promise returned by makeGeoCodingRequest
        // If you don't want to validate the data, you can in fact just return the promise variable directly
        // you probably want to return a rejected promise here if status is not what you expected
        if (data.status === "OK") return data;
            else console.error(messages[data.status]);
        return null;    
    );

您必须按以下方式致电makeGeoCodingRequest

makeGeoCodingRequest(address,bounds).then(function(data)
    // this will contain whatever 
    console.log(data);
);

【讨论】:

如果我决定使用promise.then(...)return promise,然后使用makeGeoCodingRequest(address,bounds).then(...),它还是一样吗? 是的,makeGeoCodingRequest 返回的承诺将与您的 promise.then 处理程序中的返回值具有相同的值。您可以通过返回 Q.reject(new Error("Http request failed" + data.status)) 之类的内容来拒绝 感谢您的回答,一切正常,包括 Q.reject。【参考方案2】:

我发现我得到的是一个承诺而不是一个价值

是的,因为操作是异步的,并返回一个代表未来值的承诺。

为什么没有在返回值之前执行promise.then?

因为它是异步的。 .then 将 (must) 在返回另一个 Promise 之前永远不会执行其回调。

如何从这个 Promise 而不是另一个 Promise 中获取值?

您正在回调中获取值:

makeGeoCodingRequest(address).then(function(geo) 
    console.log(geo)
    console.log(Q.isPromise(geo)); // false
    // do anything with the value here
)
// if you need to do anything with the result of the callback computation:
// you're getting back another promise for that!

不可能从 promise (many have tried) 同步获取。这意味着阻塞执行,这是我们不想要的——让我们保持异步和非阻塞!

【讨论】:

解释得很好。参考“如何从 AJAX 调用返回响应”以及与该问题相关的其他问题可能会很有趣。 您实质上是在说,您想做的任何事情都必须在then 方法中使用data 我在callback 中作为argument 获得。就像callbacks,我猜。 是的,just like callbacks。只是你得到了回调结果的另一个承诺,这使得它们可以链接。 并且您可以免费获得统一错误处理的可能性。【参考方案3】:

您正在从函数 makeGeoCodingRequest 返回一个承诺。在我看来,这是一件好事,如果将来需要,这可以帮助您链接更多的异步调用。我的建议是在返回的 Promise 上使用 .then() 并通过以下方式检查 Promise 是否有任何返回值或错误。

var geoPromise = makeGeoCodingRequest(address); 
geoPromise.then(
   onSuccess(result)
       // You can use the result that was passed from the function here.. 
   , 
   onFailure(response)
      // Handle the error returned by the function.
   
);

【讨论】:

【参考方案4】:

如果您希望代码等到异步操作完成后再继续(坏主意 - 请求完成时页面将冻结),然后将 async: false 添加到 ajax 请求的参数中。

不过,我的建议是让 makeGeoCodingRequest 直接不返回任何内容 - 并简单地给它一个额外的参数 requestCallback 以便它的调用者可以传入一个函数,该函数将在数据可用时调用。在 promise.then 函数中调用该函数,并使用结果数据。

【讨论】:

如果我的回答有问题,也许有人可以提出改进建议? 您提出同步请求的建议并未解决 OP 不理解承诺概念的事实。除此之外,它是错误的。您需要设置async:false 以实现同步请求。就像您注意到自己一样,几乎在任何情况下,这都是一个坏主意。在函数中引入回调也是一个坏主意,因为显然makeGeoCodingRequest 应该只返回一个承诺,将两者混合是一种反模式并且对初学者来说是混乱的。 啊,正确;我弄错了异步值。那部分实际上是为了解释为什么他的代码不能简单地返回一个值。我想我个人并不熟悉哪些功能应该继续使用给定的“承诺”对象,哪些应该转移到一个简单的功能的意识形态;所以这对我来说实际上是新的。我不建议他同时使用。 你的回答仍然建议他应该同时使用。 不,不是。请仔细阅读。第一段以“如果您希望您的代码等待...”开头,并且还表示这是一个坏主意。第二段是另一种选择。

以上是关于从 Promise 返回一个值的主要内容,如果未能解决你的问题,请参考以下文章

如何快速从 Promise 返回值 [重复]

在异步函数内部,从回调函数返回值返回 Promise(undefined) [重复]

在异步函数内部,从回调函数返回值返回 Promise(undefined) [重复]

如何从Promise的返回值中取到[[PromiseValue]] ?

从另一个函数中的promise内部返回一个值[重复]

关于获取Promise返回值的问题!