缓存

Classes

  • Dexie

  • DexieError

  • Collection

    • and():Add JS based criteria to collection(向集合添加基于JS的条件)
    • delete():Delete all objects in the collection(删除集合中的所有对象)
    • distinct():Remove duplicates of items with same primary key(删除具有相同主键的项的重复项)
    • modify():Modify all objects in the collection with given properties or function.(使用给定的属性或函数修改集合中的所有对象。)
    • ————————————————————————————————————————————————————————————————————
    • each():Execute query and call a function for each item(执行查询并为每个项调用函数)
    • eachKey():Execute query on the index or primary key being used and call a function for each key(对正在使用的索引或主键执行查询,并为每个键调用函数)
    • eachPrimaryKey():Execute query on the index and call a function for each primary key that corresponds to the index.(对索引执行查询,并为对应于索引的每个主键调用函数。)
    • eachUniqueKey():Execute query on the index or primary key being used and call a function for each unique key(对正在使用的索引或主键执行查询,并为每个唯一键调用函数)
    • ————————————————————————————————————————————————————————————————————
    • toArray():Execute query and get an array with the results sorted by the index used in the where() clause(执行查询并获取一个数组,其结果按where()子句中使用的索引排序)
    • desc():Sort in descending order(按降序排序)
    • reverse():Reverse the order of items.(颠倒项目顺序。)
    • sortBy():Execute query and get an array with the results sorted by given property(执行查询并获取按给定属性排序结果的数组)
    • ————————————————————————————————————————————————————————————————————
    • first():Get the first item in the collection
    • last():Get the last item in the collection(获取集合中的最后一项)
    • limit():Limit the result to given number of items(将结果限制为给定的项目数)
    • offset():Ignore N items before given offset and return the rest(忽略给定偏移量之前的n个项目并返回其余项目)
    • until():Ignores items occurring after given filter returns true.(忽略给定筛选器返回true之后发生的项。)
    • filter():Filter objects
    • ————————————————————————————————————————————————————————————————————
    • keys():Retrieve an array containing all keys of the collection (index or primary key depending on where() clause)(检索包含集合所有键的数组(索引键或主键取决于where()子句))
    • primaryKeys():Retrieve an array containing all primary keys of the collection(检索包含集合的所有主键的数组)
    • uniqueKeys():Retrieve an array containing all unique keys of the collection (index or primary key depending on where() clause)(检索包含集合所有唯一键的数组(索引键或主键取决于where()子句))
    • ————————————————————————————————————————————————————————————————————
    • count():Get the number of items in the collection(获取集合中的项数)
    • or():Logical OR operation
    • raw():Don’t filter results through reading hooks(不要通过阅读挂钩过滤结果)
    • clone():Clone the query before manipulating it further (Does not clone database items).(在进一步操作之前克隆查询(不克隆数据库项)。)
  • IndexSpec

  • Promise

  • Table

    • name:The name of the object store represented by this Table instance.
    • schema:The table schema of this object store.(此对象存储区的表架构。)
    • ————————————————————————————————————————————————————————————————————
    • hook(‘creating’):Atomic CRUD hook called when object is about to be created in db.(当对象将要在数据库中创建时调用)
    • hook(‘reading’):Atomic CRUD hook called when object has been read from db and is about to be delivered to caller.
    • hook(‘updating’):Atomic CRUD hook called when object is about to be modified in db.
    • hook(‘deleting’):Atomic CRUD hook called when object is about to be deleted from db.
    • ————————————————————————————————————————————————————————————————————
    • toArray():Get an array containing all objects in store.(获取包含存储区中所有对象的数组。)
    • limit():Return a Collection ordered by primary key, limited to N items.(返回按主键排序的集合,限制为n个项目。)
    • orderBy():Returns a Collection instance ordered by given index.(返回按给定索引排序的集合实例。)
    • offset():Return a Collection ordered by primary key, where the first N items in the table are ignored.(返回按主键排序的集合,其中表中的前n个项将被忽略。)
    • reverse():Returns a Collection instance with reversed order of the primary key.(返回主键顺序相反的集合实例。)
    • ————————————————————————————————————————————————————————————————————
    • get():Retrieve object by primary key.
    • where():Retrieve objects using a query.(使用查询检索对象。)
    • add():Insert an object into store.
    • bulkAdd():Same as add() but takes array arguments and is optimized for adding a large number of objects.(与add()相同,但接受数组参数,并针对添加大量对象进行了优化。)
    • delete():Delete an object from store.
    • bulkDelete():Same as delete() but takes and array of keys and is optimized for deleting a large number of objects.
    • put():Replace or insert object.(替换或插入对象。)
    • bulkPut():Same as put() but takes array arguments and is optimized for putting a large number of objects.
    • update():Apply given changes to an existing object.(将给定的更改应用于现有对象。)
    • clear():Clear all objects in store.
    • ————————————————————————————————————————————————————————————————————
    • defineClass():Define a javascript constructor function and map to this table.(映射表)
    • mapToClass():Map this table to javascript constructor function.(映射表)
    • count():Count all objects.
    • each():Iterate all objects in store.(迭代存储区中的所有对象。)
    • filter():Apply javascript filter on all items in the object store
    • toCollection():Get a Collection containing all objects in store.(获取包含存储区中所有对象的集合。)
  • TableSchema

  • Transaction

  • Version

  • WhereClause

    • above():Returns a collection of objects where index is above given key(返回索引位于给定键上方的对象集合)
    • aboveOrEqual():Returns a collection of objects where index is above or equal given key(返回索引高于或等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • below():Returns a collection of objects where index is below given key(返回索引低于给定键的对象集合)
    • belowOrEqual():Returns a collection of objects where index is below or equal given key(返回索引低于或等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • between():Returns a collection of objects where index is between given boundaries(返回索引位于给定边界之间的对象集合)
    • inAnyRange():Returns a collection where index is within any of the given ranges.(返回索引在任何给定范围内的集合)
    • ————————————————————————————————————————————————————————————————————
    • noneOf():Returns a collection where index equals anything but any of the keys in given array(返回一个集合,其中index等于给定数组中的任何键以外的任何值)
    • notEqual():Returns a collection where index equals anything but given value(返回一个集合,其中索引等于给定值以外的任何值)
    • ————————————————————————————————————————————————————————————————————
    • equalsIgnoreCase():Returns a collection of objects where index equals given string-key ignoring case differences(返回一个对象集合,其中索引等于给定的字符串键,忽略大小写差异)
    • equals():Returns a collection of objects where index equals given key(返回索引等于给定键的对象集合)
    • ————————————————————————————————————————————————————————————————————
    • anyOfIgnoreCase():Returns a collection of objects where index matches any of given strings, ignoring case differences.(返回一个对象集合,其中索引与任何给定字符串匹配,忽略大小写差异。)
    • anyOf():Returns a collection of objects where index is equal to any of the keys in given array(返回一个对象集合,其中索引等于给定数组中的任何键)
    • ————————————————————————————————————————————————————————————————————
    • startsWith():Returns a collection of objects where index starts with given string-key(返回以给定字符串键开头的对象集合)
    • startsWithIgnoreCase():Returns a collection of objects where index starts with given string-key ignoring case differences(返回一个对象集合,其中索引以给定的字符串键开头,忽略大小写差异)
    • ————————————————————————————————————————————————————————————————————
    • startsWithAnyOf():Returns a collection of objects where index starts with any of the given strings(返回以任何给定字符串开头的对象集合)
    • startsWithAnyOfIgnoreCase():Returns a collection of objects where index starts with any of given strings, ignoring case differences(返回一个对象集合,其中索引以任何给定字符串开头,忽略大小写差异)

Quick Reference(快速参考)

——————————————————————————————————————————————

整体说明

  • meaning that any operation that requires a result won’t be returned directly. Instead all such operations will return a Promise.(任何需要结果的操作都不会同步返回,所有这样的行为都将返回 Promise)
  • But Dexie gives you a little shortcut in all methods returning a promise with a value, so the above code will be equal to:(Dexie 为所有返回值的异步方法提供了简便方法,可以不写Promise成功时的then而改为回调函数传入方法中)
db.friends.where('name').startsWithIgnoreCase('arnold').toArray().then(function(a) {...})
// 等价于
db.friends.where('name').startsWithIgnoreCase('arnold').toArray(function(a) { // 回调函数传入toArray这个异步方法
    console.log(a.length);
}).catch(function(err) {
    console.error(err);
});

——————————————————————————————————————————————

安装

npm install dexie --save

——————————————————————————————————————————————

Declare Database(声明数据库)

  • An instance of Dexie will represent a database connection(一个Dexie 实例就是一个database 对象)
  • On your Dexie instance you will have direct access to instances of Table for each object store you have defined in your schema.(在Dexie 实例下可以直接通过属性访问定义好的各个表实例)
  • You only need to specify properties that you wish to index. The object store will allow any properties on your stored objects but you can only query them by indexed properties(只需要指定索引字段,value可以保存任意属性)
  • Never index properties containing images, movies or large (huge) strings. Store them in IndexedDB, yes! but just don’t index them!(不要把任何大数据作为索引对象,包括图片、视频、长字符串)
import Dexie from 'dexie';
var db = new Dexie("MyDatabase");
db.version(1).stores({
    friends: "++id, name, age, *tags",
    gameSessions: "id, score"
});

Understanding the flow(流程)

  • First time ◦Database is being created(创建数据库)
    • If on(‘populate’) is triggered to populate ground data.(如果有populate,触发填充初始数据)
    • db.open() promise resolves.(db.open()调用then成功)
  • Modify Schema(修改表的架构)

——————————————————————————————————————————————

The populate Event(初始化事件)

  • If database is not present, or an earlier version was present, indexedDB’s onupgradeneeded event is fired and taken care of by Dexie.(如果数据库不存在或传入更高版本号,indexedDB的 onupgradeneeded事件将被触发)
  • IndexedDB is designed for handling database creation and upgrades through the onupgradeneeded event, and define the schema there.(indexeddb设计用于通过onupgradeneeded 事件处理数据库创建和升级,并在其中定义模式。)
  • Dexie adds a declarative schema syntax on top of that so that you don’t need to subscribe to the onupgradeneeded event either.(Dexie添加了一个声明性模式语法,这样您也不需要订阅onupgradened事件。)
  • The database schema is declarative, not imperative.(数据库表架构是声明性的,不是必需的。可以在打开数据库后再创建?)
  • In case your database need initial data in order to work - data that must only be populated on database creation and never more, you can subscribe to the populate event. (如果要在数据库建立时初始化数据可以使用 populate 事件)
  • This will only be called in case the database is initially created - not when it is upgraded.(populate 事件只在建立数据库时调用,更新时不调用)
var db = new Dexie("MyTicketDB");

db.version(1).stores({
    tickets: "++id,headline,description,statusId",
    statuses: "++id,name,openess"
});

db.on("populate", function() {
    // Init your DB with some default statuses:
    db.statuses.add({id: 1, name: "opened", openess: true});
    db.statuses.add({id: 2, name: "closed", openess: false});
    db.statuses.add({id: 3, name: "resolved", openess: false});
    db.statuses.add({id: 4, name: "wontfix", openess: false});
});

——————————————————————————————————————————————

Schema Syntax(表模式的语法)

  • ++keyPathAutoincrement primary key(自动增加的主键)
  • ++Hidden autoincremented primary key(隐藏的自动增加的主键)
  • keyPathMeans that primary key can be any type and we have to provide it ourself(主键可以是任意值,新增时需要手动传入)keyPath represents a property name or a dotted path to a nested property.(keypath可以是属性名或嵌套属性的点路径。)
  • (blank)Hidden primary key(隐藏主键?)
  • &keyPathMeans that keyPath is indexed and keys must be unique(索引的值是唯一的)
  • *keyPathMeans that if key is an array, each array value will be regarded as a key to the object.(如果索引的值是一个数组,那么数组中的每一项都能索引到该行数据)This feature lacks support in IE.(此功能在IE中缺乏支持)
  • [keyPath1+keyPath2]Defining a compound index for keyPath1 and keyPath2(以keyPath1 and keyPath2定义复合索引,和and查询相同效果)his feature lacks support in IE.(此功能在IE中缺乏支持)

Indexable Types(可索引类型)

  • Only properties of certain types can be indexed. This includes string, number, Date and Array but NOT boolean, null or undefined. (能索引的类型:字符串、数值、日期、数组。不能索引的类型:布尔、null、undefined)
  • IndexedDB 2.0 contains support for indexing binary data. (IndexedDB 2.0支持二进制数据做为索引)
  • Indexing a property path that turns out to hold a non-indexable type will have no effect. (为一个属性路径建立索引,结果发现该属性路径包含一个不可索引的类型,将不会产生任何效果。)
  • 属性路径,例如:定义索引或主键为a.b,那么这张表就会认为value下的a是一个对象,并且这个对象有个属性b可以作为主键或索引。可以通过a.b这个路径直接操作表

——————————————————————————————————————————————

Class Binding(绑定类)

  • Whichever method you use, your database will return real instances of your mapped class, so that the expression(数据库获取数据操作返回的数据将是这个类的一个实例)
  • mapToClass() - map an existing class to an objectStore(将类映射到表)
class Friend {
    // Prototype method
    save() {
        return db.friends.put(this); // Will only save own props.
    }

    // Prototype property(原型属性)
    get age() { // 这里个get 是class中的一个关键字,new Friend().age时会调用该函数
        return moment(Date.now()).diff (this.birthDate, 'years'); // moment是一个日期处理库
    }
}

db.friends.mapToClass(Friend);
  • defineClass() - let Dexie declare a class for you(Dexie声明一个类)
var db = new Dexie("MyAppDB");

db.version(1).stores({
    folders: "++id,&path",
    files: "++id,filename,extension,folderId"
});

var Folder = db.folders.defineClass({
    id: Number,
    path: String,
    description: String
});

Folder.prototype.save = function () {
    return db.folders.put(this);
}

var File = db.files.defineClass({
    id: Number,
    filename: String,
    extension: String,
    folderId: Number,
    tags: [String]
});

File.prototype.save = function () {
    return db.files.put(this);
}

——————————————————————————————————————————————

Change Tracking(变动钩子)

  • With Dexie it’s possible to control and monitor each database change.(使用dexie,可以控制和监视每个数据库更改。通过钩子函数)
  • Whenever database is about to be read from or modified, they allow hook implementation to modify what will happen, or just react on the event.(当数据库即将被读取或修改时触发)
  • Hooks Documentation(钩子列表)
    • hook(‘creating’)
    • hook(‘reading’)
    • hook(‘updating’)
    • hook(‘deleting’)

——————————————————————————————————————————————

打开数据库(非必须)

  • Dexie also supports queuing operations, meaning you can start using the database directly after having defined it. In case open() hasn’t been called, it will open it automatically and enqueue the operation to execute as soon as the database is finished opening. (Dexie 支持在实例化后链式调用请求,这些请求支持依序排队执行。这时如果open未被调用,将自动打开数据库执行这些排队任务)
  • If open fails, queued operations will immediately fail with the error event from the open request.(如果打开数据库失败,排队任务也将失败,错误信息显示在oepn方法的Promise.cacth中)
var db = new Dexie("FriendsAndPetsDB");

db.version(1).stores({
    friends: "++id,name,isCloseFriend", // 自增主键和索引名
    pets: "++id,name,kind"
});
db.open();
db.friends.add({name: "Ingemar Bergman", isCloseFriend: 0});
db.pets.add({name: "Josephina", kind: "dog", fur: "too long right now"});

——————————————————————————————————————————————

Upgrade(升级)

  • 下一个版本会基于上一个版本改变,所以升级时应当保持原有版本的数据库声明不变
  • If no database present, Dexie initializes the last version directly by parsing the stores schema syntax and adding stores and indexes accordingly. No upgrade() functions run in this case.(如果新建数据库,upgrade方法不会被触发)
  • If any error occur in any upgrade function in the sequence, the upgrade transaction will roll back and db.open() will fail. (如果序列中的任何升级函数发生错误,升级事务将回滚,db.open()将失败。)
db.version(1).stores({
    friends: "++id,name,age,*tags",
    gameSessions: "id,score"
});

db.version(2).stores({
    friends: "++id, [firstName+lastName], yearOfBirth, *tags", // Change indexes(改变索引)
    gameSessions: null // Delete table(删除表)

}).upgrade(tx => {
    // Will only be executed if a version below 2 was installed.(当前浏览器数据库版本低于2时触发)
    return tx.friends.modify(friend => { // tx.friends返回的是Collection类的实例?
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        friend.birthDate = new Date(new Date().getFullYear() - friend.age, 0);
        delete friend.name; // delete是js中删除对象的关键字
        delete friend.age;
    });
});

——————————————————————————————————————————————

Add Items(添加)

  • 允许存储二级制数据,例如:new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
await db.friends.add({name: "Josephine", age: 21});

await db.friends.bulkAdd([
  {name: "Foo", age: 31},
  {name: "Bar", age: 32}
]);

——————————————————————————————————————————————

Update Items(更新)

// 替换或新增
await db.friends.put({id: 4, name: "Foo", age: 33});

await db.friends.bulkPut([
    {id: 4, name: "Foo2", age: 34},
    {id: 5, name: "Bar2", age: 44}
]);

// 根据主键更新(更新?)
await db.friends.update(4, {name: "Bar"});

// 根据搜索结果更新
await db.customers
    .where("age")
    .inAnyRange([ [0, 18], [65, Infinity] ]) // 返回索引在任何给定范围内的集合。
    .modify({discount: 0.5}); // 更新

——————————————————————————————————————————————

Delete items(删除)

// 根据主键删除
await db.friends.delete(4);

await db.friends.bulkDelete([1,2,4]);


// 根据搜索结果删除
const oneWeekAgo = new Date(Date.now() - 60*60*1000*24*7);
await db.logEntries
    .where('timestamp').below(oneWeekAgo)
    .delete();
  • 去重
db.users.where('email').startsWith('david@').distinct() // 删除具有相同主键的项的重复项?

——————————————————————————————————————————————

Query Items(查询)

  • 尽量不使用传入函数的方法,应该效率会比较低
  • 查询方式有两种
    • Table.get() - retrieve an object by its primary key.(通过主键获取)
    • Table.where() - do an advanced query.(执行高级搜索)
  • Native indexedDB has no support for logical AND or OR operations.(原生indexedDB 不支持and 和 or 查询)
  • Logical OR cannot be done by filtering - we must query the database with two queries to get it.(or 是通过两次查询取并集获得的)
  • We would gain no performance by letting the database handle Logical AND (launching two separate queries and the filter away entries that don’t exist in both collections).(and 通过js过滤性能更好)
var db = new Dexie('music');
db.version(1).stores({
    genres: '++id,name',
    albums: '++id,name,year,*tracks',
    bands: '++id,name,*albumIds,genreId'
});

async function getBandsStartingWithA () {
    const bands = await db.bands
        .where('name')
        .startsWith('A') // 返回以给定字符串键开头的对象集合
        .toArray();

    await Promise.all (bands.map (async band => { // bands.map返回[promise, ... ]
      [band.genre, band.albums] = await Promise.all([
        db.genres.get (band.genreId), // 通过主键获取
        db.albums.where('id').anyOf(band.albumIds).toArray()
      ]);
    }));
    
    return bands;
}
db.users.where('name').startsWithIgnoreCase('da') // 返回一个对象集合,其中索引以给定的字符串键开头,忽略大小写差异

const abcFriends = await db.friends
    .where("name")
    .startsWithAnyOfIgnoreCase(["a", "b", "c"]) // 返回一个对象集合,其中索引以任何给定字符串开头,忽略大小写差异
    .toArray();

await db.customers
    .where("age")
    .inAnyRange([ [0, 18], [65, Infinity] ]) // 返回索引在任何给定范围内的集合。
    .modify({discount: 0.5});
 db.friends
    .where('tags')
    .equals('close-friend')
    .primaryKeys(); // Retrieve an array containing all primary keys of the collection(返回查询结果的所有主键组成的数组)
const someFriends = await db.friends
    .where("age").between(20, 25) // 返回索引位于给定边界之间的对象集合
    .offset(150) // 忽略给定偏移量之前的n个项目并返回其余项目
    .limit(25) // 将结果限制为给定的项目数
    .toArray(); // 执行查询并获取一个数组,其结果按where()子句中使用的索引排序
db.friends.toCollection() // 返回表中所有项的集合。
    .modify(friend => { 
        // Modify each friend:
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        delete friend.name;
    });

await db.friends
    .where("name").equalsIgnoreCase("josephine") // 返回一个对象集合,其中索引等于给定的字符串键,忽略大小写差异
    .each(friend => { // 执行查询并为每个项调用函数
        console.log("Found Josephine", friend);
    });

const friendsContainingLetterA = await db.friends
    .filter(friend => /a/i.test(friend.name)) // 筛选对象
    .toArray();
await db.friends
    .where('age').above(25) // 返回age大于25
    .or('shoeSize').below(8) // 返回shoeSize小于8
    .or('interests').anyOf('sports', 'pets', 'cars') // 返回一个对象集合,其中索引等于给定数组中的任何键(数组?这个例子有问题吗?)
    .modify(friend => friend.tags.push("marketing-target")); // 使用给定的属性或函数修改集合中的所有对象。
db.friends.where('shoeSize')
    .between(37, 40)
    .or('name')
    .anyOf(['Arnold','Ingemar'])
    .and(function(friend) { return friend.isCloseFriend; }) // 传入过滤函数返回布尔值
    .limit(10)
    .each(function(friend){
        console.log(JSON.stringify(friend));
    });
const best5GameSession = await db.gameSessions
    .orderBy("score") // 根据score字段排序
    .reverse() // 颠倒项目顺序
    .limit(5) // 将结果限制为给定的项目数
    .toArray();
const forbundsKansler = await db.friends
    .where('[firstName+lastName]')
    .equals(["Angela", "Merkel"]) // 返回索引等于给定键的对象集合(复合索引时传入数组,其他传入对应数据类型)
    .first(); // 获取集合中的第一项
// In Dexie 2.0, you could do the above query a little simpler:(在新版本中可写为)
const forbundsKansler = await db.friends.where({
    firstName: "Angela",
    lastName: "Merkel"
}).first();
// 或
const forbundsKansler = await db.friends.get({ // 获取给定primarykey(主键)的对象或满足给定条件(keypath1:value1,keypath2:value2)的对象,并返回第一个匹配结果。
    firstName: "Angela",
    lastName: "Merkel"
});
// 等价于 select * from friends where firstName='Angela' order by lastName
// order by 排序
const angelasSortedByLastName = await db.friends
    .where('[firstName+lastName]')
    .between([["Angela", ""], ["Angela", "uffff"])
    .toArray()

——————————————————————————————————————————————

Transaction(事务)

  • 事务可以集合多个数据库操作,这样包裹在函数中的数据库操作就可以实现复用
  • This will not only encapsulate your changes into an atomic operation, but also optimize your code! Internally, non-transactional operations also use a transaction but it is only used in the single operation, so if you surround your code within a transaction, you will perform less costly operations in total.(对数据库的操作都是通过事务完成的,只是分为显示和隐示。把多个操作封装为事务可以时多个操作统一回滚,并且提高性能)
  • If modifying database and any error occur, every modification will be rolled back.(通过事务操作数据库,当出现错误时能够自动回滚)
  • You may do all write operations synchronically without the need to wait for it to finish before starting the next one. (see the 2nd example code below).(可以同步执行所有写操作)
  • Even read-operations can be done the line after a write operations without waiting for write to finish - still your result will include all modifications. This is possible because all operations are queued when there is a pending write operation going on in current transaction.(事务中有写操作时,所有的操作都会排队执行,所以排在后面的读操作能够读取上一条写操作写入的数据)
  • "rw" should be replaced with "r" if you are just going to do read operations.(可以用‘r’替换'rw'如果读数据是只读操作)
await db.transaction('rw', [db.friends], async () => { // 注意:这里事务操作表的声明使用了数组
  // 注意这里的事务处理函数使用了异步函数
  const friend = await db.friends.get(1);
  ++friend.age;
  await db.friends.put(friend);
});
db.transaction('rw', db.friends, db.pets, function () {  // 注意:这里事务操作表的声明使用了多参数
     // 注意这里的事务处理函数使用了同步函数
    // MAIN transaction block(主事务)
    db.transaction('rw', db.pets, function () { // 事务嵌套,如果父事务回滚了,子事务会回滚吗?
       // SUB transaction block(子事务,事务可以嵌套)
    });
});
db.transaction('rw', db.friends, function() { // 注意:这里事务操作表的声明使用了单参数
    db.friends.add({id:1, name:"Fredrik"});
    db.friends.add({id:1, name:"Fredrik"}).catch(function (err) {
        // Adding same primary key twice will of course fail. (两次添加相同的主键会失败。)
        // But since we catch this error explicitely, the transaction wont abort. (但是,由于我们捕获了这个错误,事务不会中止。)
        // This makes it possible to continue the transaction in a managed way.
        // If you still want to abort the transaction, just do Dexie.currentTransaction.abort(),(如果你想终止事务请执行Dexie.currentTransaction.abort())
        // throw an exception, or just:(重新抛出错误)
        // return Promise.reject(err);(返回Promise.reject(err))
    });
}).then (function () {
    alert ("Transaction successfully completed");
});
  • IndexedDB will commit a transaction as soon as it isn’t used within the same task.(indexeddb将在同一任务中不使用事务时立即提交该事务。)
  • News in Dexie 2.0.0-beta.6: You can now wait for other async APIs and still hold the transaction active, using Dexie.waitFor()(在Dexie 2.0.0-beta.6可以使用Dexie.waitFor()保持事务打开状态,执行非数据库操作的异步方法)
  • Make sure to use the global promise (window.Promise) within transactions. (在事务中只能使用绑定在winodw下的全局Promise)
// 该例子是错误示范,应该先把import库绑定在window上,window.Promise = Promise
// promise-polyfill-of-your-choice是低版本浏览器promise的兼容库
import Promise form 'promise-polyfill-of-your-choice'; 

db.transaction(..., ()=>{
    Promise.all()
    Promise.race() // Promise.race([promise1,promise2]) // 其中一个promise返回就会触发Promise.race改变状态
    new Promise((resolve, reject) => { ... })
})
  • In the case you write a library (not an app) and you want your library to work on old browsers without requiring a Promise polyfill, it is still safe to use Dexie.Promise:(Dexie内部提供了Dexie.Promise来兼容低版本浏览器)
db.transaction(..., ()=>{
    Dexie.Promise.all()
    Dexie.Promise.race()
    new Dexie.Promise((resolve, reject) => { ... })
})

Exception Handling(异常处理)

  • When you work with transactions, you will also get the benefit of being able to catch all errors in one single place - at the end of the transaction, instead of having to catch() every promise of each database operation. (使用事务时不需要对事务中的所有数据库操作进行错误处理,事务的异常包含内部的所有错误)
  • Any uncatuch error (no matter error events, exception or miss-spelled variable in your code) will abort the ongoing Transaction and trigger its returned Promise to reject, waking up any catch() clause attached to the transaction scope.(任何错误都会终止事务)
  • If you catch a Promise from a database operation within a transaction, it will be considered to be handled and the transaction will not be aborted.(如果在事务中捕获了异常后,事务会认为异常已经被处理,并不会终止事务)
  • If not rethrowing the error, Nils would be successfully added and transaction would commit since the error is regarded as handled when you catch the database operation.(如果没有抛出错误,事务中其他数据库操作会被执行)
  • This could be a common pitfall when people catch promises within transactions just to log it but expecting the transaction to abort. Solution: re-throw the errors that you don’t handle!(如果只是为了记录错误,可以通过重新抛出错误来终止事务)

——————————————————————————————————————————————

疑问

  • db.diary.log 表对象(Table)似乎没有这个方法?
db.transaction('rw', db.friends, db.diary, async () => {
    await spreadYourLove(); // spreadYourLove是另一个事务
    await db.diary.log({date: Date.now(), text: "Today I successfully spread my love"}); // 这里的log很奇怪?
}).catch (err => {
    console.error ("I failed to spread my love :( " + err.stack);
});
  • catch的第一个参数为什么是错误类型?
db.friends.where('name').startsWithIgnoreCase('arnold').toArray(function(a) {
    console.log(a.length);
}).catch(DOMError, function(e) {
    console.error("DOMError occurred: " + err);
}).catch(TypeError, function(e) {
    console.error("TypeError occurred: " + err);
}).catch(function(err) {
    console.error("Unknown error occurred: " + err);
}).finally(function(){
    console.log("Finally the query succeeded or failed.");
});
  • Dexie Promises supports a pattern similar to Thread-local storage where it is possible to have static properties that is bound to the executing promise and all it’s child-promises. This is similar Angular’s zone.js but in an unobtrusive way (no requirement of including any monkey-patching script). Dexie.js and it’s transaction API heavily depends on it’s transaction zones since it enables code to be aware of the currently executing transaction without having to pass transaction objects around.?
原文地址:https://www.cnblogs.com/qq3279338858/p/10980944.html