漸進式web應用開發---promise式資料庫(五)

龍恩0707發表於2019-07-22

在前面的一篇文章中,我們已經實現了使用indexedDB實現ajax本地資料儲存的功能,詳情,請看這篇文章。現在我們需要把上面的一篇文章中的程式碼使用promise結構來重構下。我們為什麼需要使用promise來重構呢?我們之前一直使用 indexedDB中的程式碼,但是在indexedDB中,我們程式碼很大程度上依賴於回撥,如果我們的程式碼越來越多,我們需要的回撥巢狀就越來越多,這對於後期程式碼維護不是很好。

比如我們可以看看我們之前的程式碼如下所示:

// 開啟或建立 store-data 資料庫
var result = window.indexedDB.open('store-data', 3);

// 監聽error函式觸發
result.onerror = function(event) {
  console.log("DataBase error:", event.target.error);
}
// 監聽當前版本號被升級的時候觸發該函式
result.onupgradeneeded = function(event) {
  var db = event.target.result;
  /*
   是否包含該物件倉庫名(或叫表名)。如果不包含就建立一個。
   該物件中的 keyPath屬性id為主鍵
  */
  if (!db.objectStoreNames.contains('store')) {
    db.createObjectStore("store", { keyPath: "id", autoIncrement: true });
  }
}

result.onsuccess = function(event) {
  var targetValue = event.target.result;
  /* 
   1. 使用 targetValue.transaction(storeName, transactionMode) 來建立事務
   2. 建立事務之後,我們使用 targetValue.transaction(storeName, transactionMode).objectStore(storeName)
   這個方法,拿到 IDBObjectStore物件。
  */
  var objectStore = targetValue.transaction(storeName, transactionMode).objectStore(storeName);
  var request = objectStore.add({id: 3, name: 'kongzhi12', age: 31});
  request.onsuccess = function(event) {
    console.log('回撥函式成功');
  }
  request.onerror = function(event) {
    console.log("DataBase error:", event.target.error);
  }
}

如上程式碼,我們開啟了或建立了一個 store-data 資料庫,然後把onsuccess回撥附加到該請求上,在該onsuccess請求上,我們又有請求事件,接著有 onsuccess 回撥函式,依次類推,如果以後程式碼越來越複雜的時候,我們以後程式碼就一直變成回撥巢狀中,因此我們現在想使用promise方法來重構上面的程式碼,我們想要讓上面的程式碼變成如下所示這樣的:

openDatabase('store-data', 3).then(function(db) {
  return openObjectStore(db, "store", "readwrite");
}).then(function(objectStore){
  return addObject(objectStore, {"id": 3, "name": 'kongzhi123', 'age': 31});
}).then(function(){
  console.log('回撥成功');
}).catch(function(error) {
  console.log('DataBase error', error);
});

我們希望變成如上的promise程式碼,我們希望將javascript非同步回撥的程式碼變成我們的promise程式碼。在重構我們的程式碼之前,我們先來看看我們的 XMLHttpRequest 程式碼,我們希望使用promise來重構該程式碼,在重構之前,我們先來看看XMLHttpRequest程式碼如下:

var xhr = new XMLHttpRequest();
xhr.onload = function() {
  // 處理響應
};
xhr.onerror = function() {
  // 處理錯誤
};
xhr.open("get", '/xxx.json', true);
xhr.send();

如上這樣的程式碼,是我們之前的xmlHttpRequest程式碼,現在我們可以使用我們的promise來重構我們上面的程式碼,因此重構後的程式碼變成如下所示:

var promise_XHR = function(url, method) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = resolve;
    xhr.onerror = reject;
    xhr.open(method, url, true);
    xhr.send();
  });
};

如上 promise_XHR 函式,該函式接收一個url 和 method引數,並返回了一個promise物件,該promise傳入了一個函式,該函式有兩個引數,代表成功和失敗的回撥,然後內部程式碼,我們建立了一個XMLHttpRequest物件,然後該物件 onload 函式的時候 把resolve 成功回撥賦值給他,然後 xhr.onerror 函式的時候,把 reject 拒絕函式傳遞給他,我們呼叫方式如下所示:

promise_XHR('/xxx.json', 'get').then(function(){
  // 處理成功的回撥函式
}).catch(error) {
  // 處理我們異常的回撥函式
}

現在我們想把該方式使用到我們的 store.js 程式碼內部來。重構後的程式碼就變成了如下:

import axios from 'axios';

var DB_VERSION = 1;
var DB_NAME = 'store-data2';

var openDataBase = function() {
  return new Promise(function(resolve, reject) {
    if (!window.indexedDB) {
      reject("indexedDB not supported");
    }
    // 開啟或建立 store-data 資料庫
    var result = window.indexedDB.open(DB_NAME, DB_VERSION);

    // 監聽error函式觸發
    result.onerror = function(event) {
      console.log("DataBase error:", event.target.error);
    }
    // 監聽當前版本號被升級的時候觸發該函式
    result.onupgradeneeded = function(event) {
      var db = event.target.result;
      /*
       是否包含該物件倉庫名(或叫表名)。如果不包含就建立一個。
       該物件中的 keyPath屬性id為主鍵
      */
      if (!db.objectStoreNames.contains('store')) {
        db.createObjectStore("store", { keyPath: "id", autoIncrement: true });
      }
    }
    result.onsuccess = function(event) {
      resolve(event.target.result);
    }
  });
};
/*
 @param {storeName} 倉庫名或表名
 @param {transactionMode} 事務模式 readOnly 只讀,readwrite 可讀可寫
*/
var openObjectStore = function(db, storeName, transactionMode) {
  return db.transaction(storeName, transactionMode).objectStore(storeName);
};

var getStore = function (successCallback) {

  return new Promise(function(resolve, reject) {
    openDataBase().then(function(db) {
      var objectStore = openObjectStore(db, 'store');
      var datas = [];
      objectStore.openCursor().onsuccess = function(event) {
        var cursor = event.target.result;
        if (cursor) {
          datas.push(cursor.value);
          cursor.continue();
        } else {
          if (datas.length > 0) {
            resolve(datas);
          } else {
            getDataFromServer().then(function(d) {
              openDataBase().then(function(db) {
                var objectStore = openObjectStore(db, "store", "readwrite");
                for (let i = 0; i < datas.length; i++) {
                  objectStore.add(datas[i]);
                }
                resolve(datas);
              });
            });
          }
        }
      }
    }).catch(function() {
      getDataFromServer().then(function(datas) {
        resolve(datas);
      });
    });
  });
};

function getDataFromServer() {
  return new Promise(function(resolve, reject) {
    axios.get("http://localhost:8081/public/json/index.json", resolve);
  });
}

var addToObjectStore = function(storeName, object) {
  return new Promise(function(resolve, reject) {
    openDataBase().then(function(db) {
      openObjectStore(db, storeName, 'readwrite').add(object).onsuccess = resolve;
    }).catch(function(error) {
      reject(error);
    })
  });
};

var updateInObjectStore = function(storeName, id, object) {
  return new Promise(function(resolve, reject) {
    openDataBase().then(function(db) {
      openObjectStore(db, storeName, "readwrite").openCursor().onsuccess = function(event) {
        var cursor = event.target.result;
        if (!cursor) {
          reject("store-data not found");
        }
        if (cursor.value.id === id) {
          cursor.put(object).onsuccess = resolve;
          return;
        }
        cursor.continue();
      }
    }).catch(function(){
      reject(error);
    })
  });
}

window.openDataBase = openDataBase;
window.openObjectStore = openObjectStore;

window.addToObjectStore = addToObjectStore;
window.updateInObjectStore = updateInObjectStore;

window.getStore = getStore;

然後我們需要在我們的 myAccount.js 程式碼改成如下初始化所示:

import $ from 'jquery';

$(function() {
  openDataBase("store-data2", 2).then(function(db) {
    return openObjectStore(db, "store", "readwrite");
  }).then(function(objectStore) {
    return addToObjectStore("store", {id: 1, name: 'kongzhi111', age: 11});
  }).then(function() {
    console.log('新增成功');
  }).catch(function(error) {
    console.log("資料庫載入失敗", error);
  });
  /*
  var addStore = function(id, name, age) {
    var obj = {
      id: id,
      name: name,
      age: age
    };
    addToObjectStore("store", obj);
    renderHTMLFunc(obj);
    $.getJSON("http://localhost:8081/public/json/index.json", obj, function(data) {
      updateDisplay(data);
    });
  };
  $("#submit").click(function(e) {
    addStore(3, 'longen1', '111');
  });
  $("#update").click(function(e) {
    $.getJSON("http://localhost:8081/public/json/index.json", {id: 1}, function(data) {
      updateInObjectStore("store", 1, data);
      updateDisplay(data);
    });
  });
  */
});

然後會向我們的資料庫中插入一條資料,我們重新整理頁面後檢視我們的本地資料庫如下所示:

 

github原始碼demo檢視

相關文章