每次需要调用IndexedDB时都要打开连接吗?

ccrfmcuu  于 2022-12-09  发布在  IndexedDB
关注(0)|答案(1)|浏览(196)

我正在开发一个简单的Angular应用程序,我想在IndexedDB中存储一些数据。
我有一个在构造函数中初始化private db: IDBDatabase;的服务:然而,当我试图在另一个方法调用中使用由构造函数初始化的DB时,这个DB从未被定义过。我知道这可能与异步调用、回调和承诺有关,但我不知道是什么...所以现在我能想到的唯一解决方案是调用

window.indexedDB.open("MyTestDatabase", 1);

每次

import {Injectable} from '@angular/core';
import {Directory, Bookmark} from "./models";
import {connectableObservableDescriptor} from "rxjs/internal/observable/ConnectableObservable";

@Injectable({
    providedIn: 'root'
})
export class BookmarksService {

    private db: IDBDatabase;
    private directoriesStoreName = "directoreis";
    private bookmarksStoreName = "bookmakrs";
    constructor() {
        console.log("Calling Bookmarks Service Constructor .... ");
        if (!window.indexedDB) {
            console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
           
        }
        let openDBRequest: IDBOpenDBRequest = window.indexedDB.open("MyTestDatabase", 1);
        console.log("Let's see if the DB will open .. ")

        /**
         * The only place where a data can be defined is onupgradeneeded callback !
         * @param event
         */
        openDBRequest.onupgradeneeded = (event: any) => {
            console.log("onupgradeneeded fired");
            this.db = event.target.result; 
        };

        openDBRequest.onsuccess = (event: any) => {
            console.log("seems that db is opened ! ");
            console.log(event);
            this.db = event.target.result;
            this.db.onerror = x => {
                console.log("An error occurred while working with DB! ");
            }
        };

        openDBRequest.onerror = event => {
            console.log("can't open IndexedDB");
            console.log(event);
        }

getAllChildDirs(parentId: number): Directory[] {

    if (this.db) { // this is NEVER defined, why ?! 
        var os = this.db.transaction(this.directoriesStoreName).objectStore(this.directoriesStoreName);
        var request = os.index("parent_id");
        var dirs: Directory[] = [];
        request.openCursor(IDBKeyRange.only(parentId)).onsuccess = (event: any) => {
            var cursor = event.target.result;
            if (cursor) {
                // cursor.key is a name, like "Bill", and cursor.value is the whole object.
                console.log("Name: " + cursor.key + ", SSN: " + cursor.value);
                dirs.push(cursor.value);
                cursor.continue();
            }
        };
        return dirs;
    }
}
    }

然后我有一个这样的组件:

export class DirectoriesListComponent implements OnInit {

    @Input() bookmarks: Bookmark[];
    @Input() directories: Directory[];
    isCollapsed = false;

    common: CommonService;
    bookmarksService: BookmarksService;

    constructor(commons: CommonService, bookmarksService: BookmarksService) {
        this.common = commons;
        this.bookmarksService = bookmarksService;
    }

    ngOnInit(): void {
        this.directories = this.bookmarksService.getAllRootDirs(); // ALWAYS returns empty array ?! becuase the db is never defined ... 
        this.bookmarks = this.bookmarksService.getBookmarks();
        //listens on when button is clicked to collapse the menu !
        this.common.dirsMenuCollapsed.subscribe(val => {
            this.isCollapsed = val
        })
    }
dced5bon

dced5bon1#

实际上,问题是你无法控制onsuccess函数何时被调用,因为它是一个回调函数,并且会在你无法控制的时候被执行。
无论您如何控制它,请查看以下内容:

  • 您承诺数据库连接打开并等待它

大概是这样的:

import {Injectable} from '@angular/core';
import {Directory, Bookmark} from "./models";
import {connectableObservableDescriptor} from "rxjs/internal/observable/ConnectableObservable";

@Injectable({
    providedIn: 'root'
})
export class BookmarksService {

  private db: IDBDatabase;
  private directoriesStoreName = "directoreis"
  private bookmarksStoreName = "bookmakrs"
  constructor() {
    console.log("Calling Bookmarks Service Constructor .... ")
    // :refac: you can even call it from the constructor, but won't wait as the constructor can't wait async functions to be completed
    this.initializeDatabase()
  }

  private async getDatabase(): Promise<IDBDatabase> {
    // :refac: Now we create a Promise<IDBDatabase> and wait for it when needed
    return new Promise<IDBDatabase>((resolve, reject) => {
      //
      const openDBRequest: IDBOpenDBRequest = window.indexedDB.open("MyTestDatabase", 1)
      console.log("Let's see if the DB will open .. ")

      /**
      * The only place where a data can be defined is onupgradeneeded callback !
      * @param event
      */
      openDBRequest.onupgradeneeded = (event: any) => {
        console.log("onupgradeneeded fired")
        const db = event.target.result
        resolve(db)
      };

      openDBRequest.onsuccess = (event: any) => {
        console.log("seems that db is opened ! ");
        console.log(event)
        const db = event.target.result
        db.onerror = x => {
          console.log("An error occurred while working with DB! ");
        }
        resolve(db)
      };

      openDBRequest.onerror = event => {
        console.log("can't open IndexedDB");
        console.log(event)
        reject()
      }
    })
  }

  async initializeDatabase(): Promise<void> {
    if (!window.indexedDB) 
      return console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")
    else if (!this.db)
      this.db = await this.getDatabase()
  }

  async getAllChildDirs(parentId: number): Promise<Directory[]> {
    await this.initializeDatabase()
    if (this.db) {
      const os = this.db.transaction(this.directoriesStoreName).objectStore(this.directoriesStoreName)
      const request = os.index("parent_id")
      const dirs: Directory[] = []
      request.openCursor(IDBKeyRange.only(parentId)).onsuccess = (event: any) => {
        const cursor = event.target.result
        if (cursor) {
          // cursor.key is a name, like "Bill", and cursor.value is the whole object.
          console.log("Name: " + cursor.key + ", SSN: " + cursor.value)
          dirs.push(cursor.value)
          cursor.continue()
        }
      }
      return dirs
    }
  }
}

当你返回一个承诺时,你应该在调用它时使用await:

async ngOnInit(): Promise<void> {
  this.directories = await this.bookmarksService.getAllRootDirs();
  this.bookmarks = await this.bookmarksService.getBookmarks();
  //listens on when button is clicked to collapse the menu !
  this.common.dirsMenuCollapsed.subscribe(val => {
    this.isCollapsed = val
  })
}

相关问题