12

我对 JavaScript 和 Promise 真的很陌生,老实说,我并不完全理解 Promise 是如何工作的,所以我需要一些帮助。

我正在使用 Google Cloud Messaging 将通知从我的站点推送给我的用户。当用户收到通知并单击它时,它会打开一个存储在 IndexedDB 中的 URL。

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()...then() 是一个承诺,但是 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('/');  
      }
    })
  );
});

但是,现在我有两个 Promise,getIdb 和 clients.matchAll,我真的不知道如何结合这两个 Promise 和两组代码。任何帮助将不胜感激。谢谢!

作为参考,这里是 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);
  });
};
4

3 回答 3

15

处理多个承诺的一种方法是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

于 2015-07-29T12:44:21.703 回答
13

event.waitUntil()接受一个承诺 - 这允许浏览器让你的工作人员保持活力,直到你完成你想做的事情(即直到你给的承诺得到event.waitUntil()解决)。

正如另一个答案所暗示的那样,您可以使用Promise.all()within event.waitUntilPromise.all()接受一个 promise 数组并返回一个 promise,所以你可以调用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);
    }
  }));
});
于 2015-07-29T14:24:59.593 回答
0

虽然我迟到了,但是,

我认为你可以使用这个为你做事的轻量级、普通的 JS 库。https://www.npmjs.com/package/easy-promise-all

这是它的小代码示例。

var { EasyPromiseAll } = require('easy-promise-all');

EasyPromiseAll({
    resultFromPromise1: Promise.resolve('first'),
    resultFromPromise2: Promise.reject('second'),
  }).then((results) => {
    const { resultFromPromise1, resultFromPromise2 } = results;
    console.log(resultFromPromise1, resultFromPromise2);
  });
于 2020-01-20T09:10:19.000 回答