javascript不能返回承诺-返回的承诺未执行

齐达尔

我有一个看起来像这样的功能

this.getToken = function() {
    if (token === null) {
        token = getAccessTokenAsync("username", "password");
        lastTokenTime = getTokenExpiryAsync();
    }
}

此函数将调用getAccessTokenAsync,它将使用xhr向我的Web服务器发出请求。看起来像这样:

getAccessTokenAsync = function (username, password) {
    var serializedData = {
        username: username, password: password,
    };

    return new WinJS.Promise(function (complete) {
        WinJS.xhr({
            type: "post",
            url: "http://127.0.0.1:8080/authenticate/login",
            responseType: "json",
            data: JSON.stringify(serializedData)
        }).done(
            function complete(result){
                return JSON.parse(result.responseText);
            }
        );
    })
}

我希望令牌现在可以在其中存储一个承诺。当我们随后调用.done()或时,.next()将具有服务器返回的json对象。但是,当我打电话时getTokenExpiryAsync(),其他事情发生了。

getTokenExpiryAsync = function () {
    if (token === null) {
        return new Date();
    }

    token.then(
        function complete(result){
            console.log(result);
        },
        function onerror(error) {
            console.log(error);
        },
        function onprogress(data) {
        });
}

相反,它似乎没有调用.then()中的任何函数,只是跳过了它!启用了严格模式,因此我的令牌变量中确实包含了一个Promise。否则会因为找不到.done()方法而出错。

我的问题是为什么要进行happering,以及如何获得所需的预期行为(可以通过其他方法访问的getAccessTokenAsync令牌中存储了promise)。

克雷格·布洛克施密特(Kraig Brockschmidt)-MSFT

在您的代码中,不必创建新的WinJS.Promise,因为WinJS.xhr()。then将返回您想要的承诺。要给出背景,有两种方法可以将完成的处理程序附加到Promise:.then和.done。两者都采用相同的参数,但返回值不同。.done返回undefined,因为它打算在promise链的最后使用。

。另一方面,当完成(或错误处理程序)返回时,.then将返回一个已兑现的承诺,而实现值是从完成的处理程序(或错误处理程序)中返回的值。

(顺便说一句,我已经写了更多关于诺言的文章,以澄清类似的问题。可以找到有关诺言的简短版本(Windows Dev Blog);更完整的版本可以在附录A“揭开承诺的神秘面纱, ”,这是我的免费电子书的“使用HTML,CSS和JavaScript编程Windows应用商店应用第二版”,目前在第二个预览版中。)

编写自己的任何异步函数时,调用其他现有的异步函数(例如WinJS.xhr)时使用的最佳模式是从其.then返回一个promise。因此,在您的情况下,您希望getAccessTokenAsync看起来像这样:

getAccessTokenAsync = function (username, password) {
    var serializedData = {
        username: username, password: password,
    };

    return WinJS.xhr({
            type: "post",
            url: "http://127.0.0.1:8080/authenticate/login",
            responseType: "json",
            data: JSON.stringify(serializedData)
        }).then(
            function complete(result){
                return JSON.parse(result.responseText);
            }
        );
    })
}

这将返回一个承诺,您将其分配给令牌,其履行值将是JSON.parse(result.responseText)的结果。

现在让我解释一下为什么您最初使用新WinJS.Promise是不正确的-我的其他著作清晰明了是一个普遍的误解。您在此处提供给构造函数的函数参数本身会接收三个参数。每个参数都是另一个函数,每个函数都称为“调度程序”,您将获得一个完整,错误和进度的函数。promise中的代码主体会在适当的事件上调用这些调度程序。

然后,这些调度程序将调用通过promise的.then或.done订阅的任何函数的完成,错误和进度处理程序。换句话说,调用这些调度程序是您实际触发对这些处理程序的调用的唯一方法。

现在,在您的原始代码中,您实际上从未真正调用任何这些代码。通过使WinJS.Promise构造函数只注意已完成的调度程序,就可以使它保持简单。但是,当您的WinJS.xhr调用完成时,您没有在调用此调度程序。造成混淆的部分原因是您有一个名为complete的参数,然后将WinJS.xhr()的完成的处理程序命名为“ complete”。如果您在上一个JSON.parse调用上设置了断点,它应该会被命中,但是您返回的值只会被吞噬,因为它从未传递给完整的调度程序

要更正此问题,您希望原始代码如下所示:

return new WinJS.Promise(function (completeDispatch) {  //Name the dispatcher for clarity
    WinJS.xhr({
        type: "post",
        url: "http://127.0.0.1:8080/authenticate/login",
        responseType: "json",
        data: JSON.stringify(serializedData)
    }).done(
        function (result) {  //Keep this anonymous for clarity
            completeDispatch(JSON.parse(result.responseText));
        }
    );
})

这也应该起作用。但是,正如我最初指出的那样,仅从WinJS.xhr()。then()返回诺言仍然是最简单的,因为您根本不需要另一个诺言包装器。

进行这些更改后,您现在应该在getTokenExpiryAsync中看到对完成的处理程序的调用。

现在让我们谈谈代码的其他部分。首先,即使存在错误条件,令牌也将始终设置为promise,因此您永远不会在getTokenExpiryAsync中看到空值。其次,如果您使用上述新的WinJS.Promise代码,您将永远不会看到错误或进度情况,因为您永远不会调用errorDispatcher或progressDispatcher。这是仅使用WinJS.xhr()。then()的返回值的另一个很好的理由。

因此,您需要在这里仔细考虑一下错误处理。到底是什么情况下您想调用新的Date()来到期的?当xhr调用失败或成功调用的响应返回空时,您会执行此操作吗?

处理错误的一种方法是使用上面带有WinJS.xhr()。done()的新WinJS.Promise变体,在该变体中,您将错误处理程序订阅到.done。然后,在该错误处理程序中,通过调用completeDispather(new Date());来确定是否要传播错误,或者是否仍要通过新的Date满足包装承诺。对于其他错误,您可以调用errorDispatcher。(请注意,所有这些都假设成功的xhr响应包含与new Date()相同的数据格式,否则,您将数据值混合在一起,并且希望从响应中解析出日期,而不仅仅是返回整个响应。)

return new WinJS.Promise(function (completeDispatch) {  //Name the dispatcher for clarity
    WinJS.xhr({
        type: "post",
        url: "http://127.0.0.1:8080/authenticate/login",
        responseType: "json",
        data: JSON.stringify(serializedData)
    }).done(
        function (result) {  //Keep this anonymous for clarity
            completeDispatch(JSON.parse(result.responseText));
        },
        function (e) {
            completeDispatch(new Date());  //Turns an xhr error into success with a default.
        }
    );
})

我刚刚描述的内容确实是捕获核心操作中的错误然后注入默认值的好方法,这是我认为您想要的。

另一方面,如果您使用WinJS.xhr()。then()的返回值(第一个代码变体),则需要将更多此类逻辑放入getTokenExpiryAsync中。(顺便说一句,正如您所展示的,此代码是同步的,一个代码路径返回一个新的Date,另一个返回未定义的路径,因此它不是您想要的。)

现在,因为令牌本身就是一个承诺,这getTokenExpiryAsync确实需要异步本身,因此需要同时返回到期的承诺。这是您的写法:

function getTokenExpiryAsync (token) { //I'd pass token as an argument here
    return token.then(
        function complete(result) {
            return result; //Or parse the date from the original response.
        },
        function error(e) {
            return new Date(); 
        }
    );
}

然后,在您的调用代码中,您需要说:

getTokenExpiryAsync(token).then(function (expiry) {
    lastTokenTime = expiry;
}

再次,我们利用返回值作为另一个promise,该promise的实现值是从completed方法或error方法返回的值。如果令牌处于错误状态(WinJS.xhr失败),则对.then的调用将调用错误处理程序,然后在其中返回所需的默认值。否则,您将从响应中返回您想要的任何到期时间。无论哪种方式,您都可以在原始调用代码中从.then获得此承诺的日期。

我知道这可能有点令人困惑,但这是Promises / A规范和异步编码的本质,尤其不是WinJS。

希望所有这些都值得您的赏金。:)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章