javascript返回一个promise的麻烦 - 返回的promise没有执行

zid*_*sal 3 javascript winjs

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

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

此函数将调用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);
            }
        );
    })
}
Run Code Online (Sandbox Code Playgroud)

我希望令牌现在可以存储一个承诺.当我们调用.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) {
        });
}
Run Code Online (Sandbox Code Playgroud)

相反它似乎没有调用.then()中的任何函数它只是跳过它!启用了严格模式,因此我的令牌变量在其中具有承诺.否则它会出错,因为它无法找到.done()方法?

我的问题是为什么这是happerning,我怎么能得到我想要的预期行为(令牌从getAccessTokenAsync存储在其中的承诺,我可以在其他方法中访问).

Kra*_*SFT 5

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

另一方面,返回一个在完成(或错误处理程序)返回时满足的promise,并且履行值是从已完成的处理程序(或错误处理程序)返回的值.

(顺便说一下,我已经写了很多关于承诺来澄清这样的问题.可以找到一个简短的版本所有关于promises(Windows开发博客);更完整的版本可以在附录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);
            }
        );
    })
}
Run Code Online (Sandbox Code Playgroud)

这将返回一个promise,您分配给token,其履行值将是JSON.parse(result.responseText)的结果.

现在让我解释为什么你最初使用新的WinJS.Promise是不正确的 - 这是一个常见的误解,我的其他着作清晰.你在这里给构造函数赋予的函数参数接收三个参数.每个参数都是另一个函数,我称之为"调度程序",你得到一个完整,错误和进度的函数.承诺中的代码主体会在适当的事件上调用这些调度程序.

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

现在,在原始代码中,您实际上从未调用任何这些代码.通过让WinJS.Promise构造函数只关注已完成的调度程序,您可以保持简单.但是,当您的WinJS.xhr调用完成后,您不会调用此调度程序.令人困惑的部分原因是你有一个名为complete的参数,然后为WinJS.xhr()命名你已完成的处理程序.完成"完成".如果你在最后一次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));
        }
    );
})
Run Code Online (Sandbox Code Playgroud)

这也应该有用.但是,最简单的方法是从WinJS.xhr()返回承诺.然后()我最初注意到,因为你根本不需要另一个promise包装器.

通过这些更改之一,您现在应该在getTokenExpiryAsync中看到对已完成的处理程序的调用.

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

所以你需要在这里仔细考虑你的错误处理.确切地说,您希望为到期日调用新的Date()的情况是什么?当xhr调用失败,或者成功调用的响应返回空时,您是否这样做?

处理错误的一种方法是使用上面的新WinJS.Promise变体,使用WinJS.xhr().done(),在其中为.done订阅错误处理程序.在该错误处理程序中,您可以通过调用completeDispather(new Date());来确定是否要传播错误,或者是否仍希望通过新Date来实现包装器承诺.对于其他错误,您将调用errorDispatcher.(请注意,所有这些都假定成功的xhr响应包含与新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.
        }
    );
})
Run Code Online (Sandbox Code Playgroud)

我刚刚描述的是一个很好的方法来捕获核心操作中的错误,然后注入一个默认值,这是我认为你打算的.

如果你使用WinJS.xhr().then()的返回值,另一方面(第一个代码变体),那么你需要将更多的这个逻辑放在getTokenExpiryAsync中.(顺便说一句,这个代码,如你所示,是同步的,一个代码路径返回一个新的Date而另一个返回undefined,所以它不是你想要的.)

现在因为令牌本身是一个承诺,这个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(); 
        }
    );
}
Run Code Online (Sandbox Code Playgroud)

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

getTokenExpiryAsync(token).then(function (expiry) {
    lastTokenTime = expiry;
}
Run Code Online (Sandbox Code Playgroud)

我们再次利用返回值作为另一个承诺,其履行价值是从已完成或错误方法返回的.如果令牌处于错误状态(WinJS.xhr失败),那么您对.then的调用将调用错误处理程序,然后返回所需的默认值.否则,您将从响应中返回所需的任何到期日期.无论哪种方式,您都可以在原始调用代码中从.then获取此承诺的日期.

我知道这可能有点令人困惑,但它是Promises/A规范和异步编码的本质,特别是WinJS.

希望这一切都值得您的赏金.:)