ダイアログをはさみながらの非同期処理の再実行

接続ボタンを押したら、XMLHttpRequestを飛ばして取得したHTMLのサイズを表示するというものです。
タイムアウトが起きたら、ダイアログを表示して再接続するかどうかをユーザーに尋ねます。
再接続でまたタイムアウトしたら同じダイアログを出すということを繰り返します。

こういう画面です。


Access is denied のエラーで困りましたが、これでいいんじゃないかなー?
ナビゲーションアプリケーションのテンプレートのhome.htmlとhome.jsを変更して試しました。

home.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <title>ホーム ページ</title>

    <!-- WinJS 参照 -->
    <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="/css/default.css" rel="stylesheet" />
    <link href="/pages/home/home.css" rel="stylesheet" />
    <script src="/pages/home/home.js"></script>
</head>
<body>
    <!-- 読み込まれて表示されるコンテンツです。 -->
    <div class="fragment homepage">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="pagetitle">再接続サンプルへようこそ!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
            <button id="connectButton">接続</button>
            <div>サイズ:<span id="output"></span></div>
        </section>
    </div>
</body>
</html>
home.js
(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/home/home.html", {
        ready: function (element, options) {
            element.querySelector("#connectButton").addEventListener("click", click.bind(this));
        }
    });

    function click(event) {
        // 連打されると後続の処理でMessageDialogが複数開かれることになり
        // 「0x80070005 - JavaScript runtime error: Access is denied.」といったエラーが発生するので
        // disabledの制御をする。
        event.target.disabled = true;

        var outputElement = this.element.querySelector("#output");
        outputElement.textContent = "";

        fetch("http://www.microsoft.com", timeoutHandler).then(
            function (html) {
                if (html) {
                    outputElement.textContent = html.length;
                }
            }
        ).done(enable, enable);

        function enable() {
            event.target.disabled = false;
        }
    }

    function fetch(url, timeoutHandler) {
        var connectComplete;
        var connectPromise = new WinJS.Promise(function (c) { connectComplete = c; });
        var timeout = 100;
        connect();
        // Promiseを返すが、Promiseは接続が成功するまで完了させない。
        return connectPromise;

        function connect() {
            timeout *= 1.5;
            return WinJS.Promise.timeout(timeout, WinJS.xhr({ url: url }).then(
                function complete(result) {
                    return connectComplete(result.status === 200 ? result.responseText : "");
                },
                function error(error) {
                    if (error.message === "Canceled") {
                        timeoutHandler(connect, cancel);
                        return null;
                    }
                    cancel();
                }
            ));
        };

        function cancel() {
            console.log("キャンセル");
            connectPromise.cancel();
        }
    }

    function timeoutHandler(retry, cancel) {
        var msg = new Windows.UI.Popups.MessageDialog("タイムアウトしました");
        msg.commands.append(new Windows.UI.Popups.UICommand("再接続", function () {
            console.log("再接続");
        }, "retry"));
        msg.commands.append(new Windows.UI.Popups.UICommand("閉じる", function () {
            console.log("閉じる");
        }));
        msg.defaultCommandIndex = 0;
        msg.cancelCommandIndex = 1;
        msg.showAsync().done(function (command) {
            if (command && command.id === "retry") {
                // 再接続は、ダイアログが閉じられた後で実行する。
                // そうしないと再度timeoutHandlerが呼ばれた場合にMessageDialogが二重で開かれることになり
                //「0x80070005 - JavaScript runtime error: Access is denied.」といったエラーが発生するため。
                retry();
            } else {
                // Promiseをエラーで終了させるため、cancelを呼び出す。
                cancel();
            }
        });
    }

})();