javascript - javascript不能返回 promise -返回的 promise 未执行

标签 javascript winjs

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

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);
            }
        );
    })
}

我希望 token 现在可以在其中存储一个 promise 。然后当我们调用.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()中的任何函数,只是跳过了它!启用了严格模式,因此我的 token 变量中确实包含有promise。否则会因为无法找到.done()方法而出错。

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

最佳答案

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

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

(顺便说一句,我已经写了很多关于 promise 的说明,以澄清这样的问题。可以在All about promises(Windows Dev Blog)中找到一个简短的版本;在以下内容的附录A“揭密 promise ”中可以找到更完整的版本。我的免费电子书Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Edition,目前在第二个预览版中。)

编写自己的任何异步函数时,调用其他现有异步函数(例如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);
            }
        );
    })
}

这将返回一个 promise ,您将其分配给 token ,其履行值将是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()返回 promise 仍然是最简单的,因为您根本不需要另一个 promise 包装器。

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

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

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

处理错误的一种方法是使用上面带有WinJS.xhr()。done()的新WinJS.Promise变体,在其中将错误处理程序订阅到.done。然后,在该错误处理程序中,通过调用completeDispather(new Date());来确定是要传播错误,还是要用新的Date继续实现包装 promise 。对于其他错误,您可以调用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.
        }
    );
})

我刚刚描述的是在核心操作中捕获错误并随后注入(inject)默认值的好方法,这是我认为您想要的。

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

现在,由于 token 本身就是一个 promise ,因此此getTokenExpiryAsync确实需要异步,因此还需要为到期返回一个 promise 。这是您的写法:
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方法返回的值。如果 token 处于错误状态(WinJS.xhr失败),则对.then的调用将调用错误处理程序,然后在其中返回所需的默认值。否则,您将从响应中返回想要的任何到期时间。无论哪种方式,您都可以在原始调用代码中从.then获得此 promise 的日期。

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

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

关于javascript - javascript不能返回 promise -返回的 promise 未执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19686414/

相关文章:

windows-8 - 禁用 ListView 中特定项目的选择和 itemInvoke 事件

javascript - 如何确定 WebAudio 振​​荡器是否静音?

javascript - 在 App 脚本中读取 HashMap

javascript - Apollo-client _mutation 输入类型 - 提供的数据为空

geolocation - 用户拒绝一次后如何再次询问用户使用地理定位的权限?

javascript - 使用 WinJS.xhr 将文件放入休息服务

windows-8 - WinJS 不卸载 js/css

JavaScript 将函数中的元素附加到发送者

javascript - 正则表达式从 URL 中提取匹配的字符串(工作,更好的解决方案?)

javascript - 如何使用 Javascript 手动显示 Windows 8 Metro 加载轮/加载点