Цепочка IndexedDB в WinJS
Я пишу приложение для Windows 8 на HTML / JS и на форме есть кнопка с обработчиком события щелчка. При нажатии первая вещь, которую делает кнопка, это:
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");
}
}
(У меня также есть обратные вызовы onerror и onupgradeneeded для объекта req, но я опущу их для краткости).
Я явно не понимаю, как обещания должны работать, но я подумал, что смогу связать несколько ТОП-вызовов на обещание, и последний вызов DONE будет срабатывать только после того, как все вызовы THEN выполнены. Проблема в том, что консоль показывает "openDb...", затем "PROMISE DONE", затем "OpenDb done". Таким образом, вызов DONE выполняется перед вызовом THEN. Кто-нибудь может объяснить, почему это происходит?
1 ответ
Ваша основная проблема заключается в том, что метод then возвращает новое обещание. Если функция, которую вы вызвали, возвращает обещание, то это обещание возвращается (и, таким образом, связывается). Если ваша функция не возвращает обещание, возвращается новое (уже выполненное) обещание, которое возвращает вам возвращаемое значение.
Глядя на вашу функцию openDB, что она возвращает? На самом деле, он возвращает неопределенное. И что еще более важно, он запускает асинхронную работу и затем немедленно возвращается; асинхронная операция не завершается до позже. Таким образом, вы получаете поведение, которое вы видите.
Поэтому вам нужно заставить openDB вернуть обещание, которое не будет выполнено до завершения операции открытия базы данных. Обещания WinJS действительно плохи в этом, поэтому API уродлив. Надеюсь, они восполнят недостающую часть в Win8.1.
Итак, вам нужно создать новое обещание и выполнить его после завершения асинхронной работы. Это выглядит примерно так:
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) { ... })
call создает новый объект обещания. Передаваемой вами функции передаются три функции - одна для вызова при успешном завершении объекта (c для завершения), одна для вызова, если обещание не выполнено успешно (e для ошибки), и одна для вызова, чтобы сообщить о прогрессе (p для прогресса)). Мы перенесем обратный вызов завершения в переменную здесь для использования позже.
Теперь при успешном обратном вызове из indexedDB обратите внимание, что мы вызываем полный обратный вызов, передавая полученные данные. Это установит обещание к завершенному состоянию.
Наконец, мы возвращаем созданное обещание. Обратите внимание, что этот возврат происходит синхронно; функция возвращается до вызова обработчика onsuccess.
Это должно дать вам то, что вы хотите - оно будет сдерживать цепочку обещаний, пока не завершится открытие базы данных. Вам, вероятно, следует также подключить обработчик ошибок к чему-либо.
Теперь, сделав это, вызов WinJS.Promise.then() также является неправильным или, по крайней мере, ненужным. Вместо этого вы должны просто сделать:
openDB().done(function () { console.log('Promise Done'); });
Поскольку сама openDB возвращает обещание, вам не требуется дополнительная упаковка в другое обещание, которое вы делаете.