2015-07-29 4 views
3

Я действительно новичок в JavaScript и обещаю, и, честно говоря, я не совсем понимаю, как работают обещания, поэтому мне нужна помощь.Объединение двух обещаний

Я использую Google Cloud Messaging для отправки уведомлений с моего сайта моим пользователям. Когда пользователи получают уведомление и нажимают на него, он открывает URL-адрес, хранящийся в IndexedDB.

importScripts('IndexDBWrapper.js'); 
var KEY_VALUE_STORE_NAME = 'key-value-store', idb; 

function getIdb() { 
    if (!idb) { 
    idb = new IndexDBWrapper(KEY_VALUE_STORE_NAME, 1, function (db) { 
     db.createObjectStore(KEY_VALUE_STORE_NAME); 
    }); 
    } 
    return idb; 
} 

self.addEventListener('notificationclick', function (event) { 
    console.log('On notification click: ', event); 
    event.notification.close(); 
    event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag).then(function (url) { 
    var redirectUrl = '/'; 
    if (url) redirectUrl = url; 
     return clients.openWindow(redirectUrl); 
    })); 
}); 

Таким образом, в приведенном выше коде, я знаю, что getIdb() ... то() это обещание, но это event.waitUntil также обещание?

Проблема с вышеуказанным кодом заключается в том, что он открывает экземпляр Chrome каждый раз, когда нажимается уведомление, и я бы предпочел, чтобы он использовал существующий экземпляр, если он доступен. Далее делает только что:

self.addEventListener('notificationclick', function(event) { 
    console.log('On notification click: ', event.notification.tag); 
    event.notification.close(); 
    event.waitUntil(
    clients.matchAll({ 
     type: "window" 
    }) 
    .then(function(clientList) { 
     for (var i = 0; i < clientList.length; i++) { 
     var client = clientList[i]; 
     if (client.url == '/' && 'focus' in client) 
      return client.focus(); 
     } 
     if (clients.openWindow) { 
     return clients.openWindow('/'); 
     } 
    }) 
); 
}); 

Однако, теперь у меня есть два обещания, getIdb и clients.matchAll, и я действительно не имею ни малейшего представления о том, как совместить эти два обещание и два набора кода. Любая помощь будет принята с благодарностью. Благодаря!

Для справки, здесь IndexDBWrapper.js:

'use strict'; 

function promisifyRequest(obj) { 
    return new Promise(function(resolve, reject) { 
    function onsuccess(event) { 
     resolve(obj.result); 
     unlisten(); 
    } 
    function onerror(event) { 
     reject(obj.error); 
     unlisten(); 
    } 
    function unlisten() { 
     obj.removeEventListener('complete', onsuccess); 
     obj.removeEventListener('success', onsuccess); 
     obj.removeEventListener('error', onerror); 
     obj.removeEventListener('abort', onerror); 
    } 
    obj.addEventListener('complete', onsuccess); 
    obj.addEventListener('success', onsuccess); 
    obj.addEventListener('error', onerror); 
    obj.addEventListener('abort', onerror); 
    }); 
} 

function IndexDBWrapper(name, version, upgradeCallback) { 
    var request = indexedDB.open(name, version); 
    this.ready = promisifyRequest(request); 
    request.onupgradeneeded = function(event) { 
    upgradeCallback(request.result, event.oldVersion); 
    }; 
} 

IndexDBWrapper.supported = 'indexedDB' in self; 

var IndexDBWrapperProto = IndexDBWrapper.prototype; 

IndexDBWrapperProto.transaction = function(stores, modeOrCallback, callback) { 
    return this.ready.then(function(db) { 
    var mode = 'readonly'; 

    if (modeOrCallback.apply) { 
     callback = modeOrCallback; 
    } 
    else if (modeOrCallback) { 
     mode = modeOrCallback; 
    } 

    var tx = db.transaction(stores, mode); 
    var val = callback(tx, db); 
    var promise = promisifyRequest(tx); 
    var readPromise; 

    if (!val) { 
     return promise; 
    } 

    if (val[0] && 'result' in val[0]) { 
     readPromise = Promise.all(val.map(promisifyRequest)); 
    } 
    else { 
     readPromise = promisifyRequest(val); 
    } 

    return promise.then(function() { 
     return readPromise; 
    }); 
    }); 
}; 

IndexDBWrapperProto.get = function(store, key) { 
    return this.transaction(store, function(tx) { 
    return tx.objectStore(store).get(key); 
    }); 
}; 

IndexDBWrapperProto.put = function(store, key, value) { 
    return this.transaction(store, 'readwrite', function(tx) { 
    tx.objectStore(store).put(value, key); 
    }); 
}; 

IndexDBWrapperProto.delete = function(store, key) { 
    return this.transaction(store, 'readwrite', function(tx) { 
    tx.objectStore(store).delete(key); 
    }); 
}; 
+0

Это звучит как [ 'event.waitUntil'] (https://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil) - это метод, который * принимает * обещание. – Bergi

+0

@Bergi спасибо за подсказку. Знание реальных имен этих вещей очень поможет мне. –

ответ

8

event.waitUntil() принимает обещание - это позволяет браузеру поддерживать работоспособность вашего работника, пока вы не закончите то, что хотите (т.е. пока выпуск ise, что вы дали event.waitUntil()).

Как следует из другого ответа, вы можете использовать Promise.all() в пределах event.waitUntil. Promise.all() берет ряд обещаний и возвращает обещание, поэтому вы можете называть его then. Ваша функция обработки получит множество результатов обещаний, когда все обещания, которые вы предоставили Promise.all, разрешились. Ваш код будет выглядеть примерно так (я на самом деле не проверял, но это должно быть близко):

self.addEventListener('notificationclick', function (event) { 
    event.notification.close(); 
    event.waitUntil(Promise.all([ 
     getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag), 
     clients.matchAll({ type: "window" }) 
    ]).then(function (resultArray) { 
    var url = resultArray[0] || "/"; 
    var clientList = resultArray[1]; 
    for (var i = 0; i < clientList.length; i++) { 
     var client = clientList[i]; 
     if (client.url == '/' && 'focus' in client) 
     return client.focus(); 
    } 
    if (clients.openWindow) { 
     return clients.openWindow(url); 
    } 
    })); 
}); 
+0

Я просмотрел Promise.all(), но у меня был мой .then() в массиве обещаний, а не на. Ваш код делает трюк. Я постепенно начинаю понимать обещания. Благодаря! –

+0

Нет проблем. Лично я нашел [главу о обещаниях] (https://github.com/getify/You-Dont-Know-JS/blob/master/async%20&%20performance/ch3.md) в * You Do not Know JS : Async and Performance * очень полезно, особенно после прочтения его дважды! –

+0

Теперь мне просто нужно подождать, когда будет реализован метод 'client.navigate (url)'. Когда он будет реализован, каков наилучший способ вернуть как «client.navigate (url)», так и «client.focus()»? –

2

Один из способов справиться с множеством обещаний с Promise.all

Promise.all([promise0, promise1, promise2]).then(function(valArray) { 
    // valArray[0] is result of promise0 
    // valArray[1] is result of promise1 
    // valArray[2] is result of promise2 
}); 

читать о promise.all - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

+0

Обратите внимание, что 'Promise.all' выполняет итерацию (например, массив), но не несколько аргументов – Bergi

+0

да, для этого я первоначально имел обещание и функцию then с несколькими аргументами (прошло некоторое время с тех пор, как я использовал он) и фиксированный, но не другой –