0

我正在用 HTML / JS 编写一个 Windows 8 应用程序,并在带有点击事件处理程序的表单上有一个按钮。单击按钮时,该按钮所做的第一件事是:

WinJS.Promise.then(openDB()).done(console.log("PROMISE DONE"));

openDB 函数如下所示:

function openDB() {
    console.log("openDb...");
    var req = indexedDB.open("MyDB", 1);
    req.onsuccess = function(evt) {
        var data = evt.target.result;
        console.log("openDb DONE");
    }
}

(我在 req 对象上也有 onerror 和 onupgradeneeded 回调,但为简洁起见将它们省略了)。

我显然误解了承诺应该如何工作,但我认为我可以在一个承诺上链接多个 THEN 调用,并且最终调用 DONE 只会在所有 THEN 调用都执行后才会触发。问题是控制台显示'openDb ...',然后是'PROMISE DONE',然后是'OpenDb done'。所以 DONE 调用是在 THEN 调用之前执行的。谁能解释为什么会这样?

4

1 回答 1

1

您在这里的基本问题是“then”方法返回一个新的承诺。如果您调用的函数返回一个承诺,那就是返回的承诺(并因此被链接)。如果您的函数没有返回承诺,则会返回一个新的(已经完成的)承诺,为您提供返回的值。

查看您的 openDB 函数,它返回什么?实际上,它返回未定义。更重要的是,它启动异步工作,然后立即返回;异步操作直到稍后才会完成。因此,您得到了您所看到的行为。

因此,您需要做的是让 openDB 返回一个在数据库打开操作完成之前不会完成的承诺。WinJS Promise 在这方面真的很糟糕,所以 API 很丑陋。希望他们能填补 Win8.1 中缺失的部分。

因此,您需要创建一个新的 Promise,并在异步工作完成后完成它。看起来像这样:

function openDB() {
    var complete;
    var = new Promise(function (c, e, p) {
        complete = c;
    });

    console.log("openDb...");
    var req = indexedDB.open("MyDB", 1);
    req.onsuccess = function(evt) {
        var data = evt.target.result;
        console.log("openDb DONE");
        complete(data);
    }

    return p;
}

new Promise(function (c, e, p) { ... })调用创建了一个新的 Promise 对象。您传递的函数本身传递了三个函数 - 一个在对象成功完成时调用(c 表示完成),一个在 promise 未成功完成时调用(e 表示错误),以及一个用于报告进度(p 表示进度)。我们在这里将完成回调存储到一个变量中以供以后使用。

现在,在 indexedDB 的成功回调中,请注意我们调用了完整的回调,传递了我们获得的数据。这会将承诺设置为已完成状态。

最后,我们返回创建的 Promise。请注意,此返回是同步发生的;该函数在调用 onsuccess 处理程序之前返回。

这应该会得到你想要的 - 它会推迟承诺链,直到数据库打开完成。您可能也应该将错误处理程序连接到某些东西。

现在,完成此操作后,对 WinJS.Promise.then() 的调用也是不正确的,或者至少是不必要的。你应该这样做:

openDB().done(function () { console.log('Promise Done'); });

由于 openDB 本身会返回一个承诺,因此您不需要在您正在执行的另一个承诺中进行额外的包装。

于 2013-07-22T18:14:37.907 回答