Promise encapsulation of indexeddb transaction function (2) — using generator to complete synchronization transformation

Time:2021-7-8

preface

Originally, this series should not be updated so quickly, but I found a huge loophole in my thinking after I finished the previous one last night. As a result, I spent a lot of time in the previous article implementing a complex transaction class – there is a much simpler way to do it.
First part:http://segmentfault.com/a/1190000003982058
Project address:https://github.com/woodensail/indexedDB

Yesterday’s negligence

When designing the package library yesterday, I intended to use it at the beginningFront end implementation of CO moduleThe async library implemented in is used to complete the synchronization of database access. However, after trying it, we found that it is not feasible. The problem was mentioned in the previous article, which lies in the mechanism of promise. The conclusion is that indexeddb cannot be encapsulated with native promise.
On the premise that promise can not be used, neither synchronization nor chain operation can be used. So yesterday, I realized a simple promise of synchronous execution, and realized chain operation based on it.
After writing yesterday’s article, it suddenly occurred to me that since there is no promise, it can’t be realizedSynchronizationandChain operationThen I can implement promise in one step without using chain operationSynchronization。 So this article is about how to complete the indexeddb synchronization encapsulation.

Use examples

This is the most basic usage. It still implements the get, put, getkv, putkv functions as it did yesterday.
In this way, each operation is a separate transaction, which cannot guarantee atomicity.

async(function* () {
    var db = yield new DB('test');
    yield db.put('info', {k: 'async', v: 1});
    var result1 = yield db.get('info', 'async');
    console.log(result1.v);
    
    yield db.putKv('info', 'async', '1');
    var result2 = yield db.getKv('info', 'async');
    console.log(result2);
}).catch(function (e) {
    console.log(e);
});

First, open a transaction and pass it to the database access function as a parameter to enable transaction support.
The following two operations are in the same transaction.

async(function* () {
    var db = yield new DB('test');
    var tx = db.transaction('info', 'readwrite');
    yield db.put('info', {k: 'async', v: 1}, tx);
    var result1 = yield db.get('info', 'async', tx);
    console.log(result1.v);
}).catch(function (e) {
    console.log(e);
});

When a transaction is started, the third parameter is filled with true, and the current transaction can be used as the default transaction. Later operations will automatically use the transaction. You need to call transactionEnd manually to clear the default transaction after use.

async(function* () {
    var db = yield new DB('test');
    db.transaction('info', 'readwrite', true);
    yield db.put('info', {k: 'async', v: 1});
    var result1 = yield db.get('info', 'async');
    console.log(result1.v);
    db.transactionEnd();
}).catch(function (e) {
    console.log(e);
});

Implement promise

This is an incomplete version of promise implementation, which only provides the most basic then and catch and their chained calls. It’s enough for async anyway.
There is no support for promise. All at the moment. I think we have to modify the async library to support it, so let’s talk about it later.

var DbPromise = function (fun) {
    this.state = 'pending';
    this.resolveList = [];
    this.rejectList = [];
    var _this = this;
    fun(function () {
        _this.resolve.apply(_this, arguments)
    }, function () {
        _this.reject.apply(_this, arguments)
    });
};
DbPromise.prototype = {};
DbPromise.prototype.resolve = function (data) {
    this.state = 'resolved';
    this.data = data;
    var _this = this;
    this.resolveList.forEach(function (fun) {
        _this.data = fun(_this.data)
    });
};
DbPromise.prototype.reject = function (data) {
    this.state = 'rejected';
    this.error = data;
    this.rejectList.forEach(function (fun) {
        fun(data);
    });
};
DbPromise.prototype.then = function (fun) {
    if (this.state === 'pending') {
        this.resolveList.push(fun);
    } else {
        this.data = fun(this.data);
    }
    return this;
};
DbPromise.prototype.catch = function (fun) {
    if (this.state === 'pending') {
        this.rejectList.push(fun);
    } else {
        fun(this.error);
    }
    return this;
};

Implementation of database encapsulation class

Define class dB. The operation of opening database is defined in_ Open, which will be introduced later

var DB = function (name, upgrade, version) {
    var _this = this;
    //You can pre define the database in other JS files by adding members to DB. Dbmap
    //If it is a predefined database, you can call new DB (name) to open it
    //Otherwise, you need to call new DB (name, upgrade, version) to fill in the upgrade response function and version number
    if (DB.dbMap[name]) {
        var map = DB.dbMap[name];
        return _open(name, map.upgrade, map.version).then(function (db) {
            _this.db = db;
            return _this;
        }).then(map.nextStep);
    } else {
        return _open(name, upgrade, version).then(function (db) {
            _this.db = db;
            return _this;
        });
    }
};
DB.prototype = {};

Methods to open and close a transaction.

DB.prototype.transaction = function (table, type, asDefault) {
    //Open the transaction of the current database according to the given target and type
    var tx = _transaction(this.db, table, type);
    //If asdefault is true, the transaction is cached as the default transaction.
    //Note that setting multiple default transactions at the same time will conflict,
    //In theory, there is a very small possibility that this will happen in asynchronous operations, and I will correct this in the next version.
    if (asDefault) {
        this.tx = tx;
    }
    return tx;
};
//Cancel default transaction
DB.prototype.transactionEnd = function () {
    this.tx = void 0;
};
function _transaction(db, table, type) {
    return db.transaction(table, type);
}

Specific database operation function. That’s right_ The encapsulation of put and other functions.

//TX || this.tx means that the transaction specified by the parameter is used first, followed by the default transaction
DB.prototype.put = function (table, data, tx) {
    return _put(this.db, table, data, tx || this.tx);
};
DB.prototype.get = function (table, name, tx) {
    return _get(this.db, table, name, tx || this.tx);
};
DB.prototype.clear = function (table, tx) {
    return _clear(this.db, table, tx || this.tx);
};

//These two are special encapsulation of get and put, which have more preprocessing of parameters and results, and simplify the format of parameters and return values
DB.prototype.getKv = function (table, k, tx) {
    return _get(this.db, table, k, tx).then(o=>(o || {}).v);
};
DB.prototype.putKv = function (table, k, v, tx) {
    return _put(this.db, table, {k, v}, tx);
};

_ open,_ put,_ get,_ The implementation of clear is similar to the latter three, so it only pastes_ put。 If you need the last two codes, please check GitHub.

function _open(name, upgrade, version) {
    //Returns the custom promise to be called by async library
    return new DbPromise(function (resolve, reject) {
        //Opens the specified version of the specified database
        var request = indexedDB.open(name, version);
        //Set up upgrade operation
        request.onupgradeneeded = upgrade;
        //Bind success and error, where the open database object will be returned after success
        request.onsuccess = function (e) {
            resolve(request.result);
        };
        request.onerror = reject;
    });
}
function _put(db, table, data, tx) {
    //Returns the custom promise to be called by async library
    return new DbPromise(function (resolve, reject) {
        //If no transaction is provided, a new transaction is created
        tx = tx || db.transaction(table, 'readwrite');
        //Open the store and operate
        var store = tx.objectStore(table);
        var request = store.put(data);
        //Set callback
        request.onsuccess = function () {
            resolve(request.result);
        };
        request.onerror = reject;

    });
}

summary

Basically, after the implementation of dbpromise, the implementation of other parts is the same as before, which is very simple. I just had a cramp in my head on singles day yesterday.
Now the library can be used as a basic key value database. I will add more methods in the future. Dear friends,Recommend or collect it

Recommended Today

Implementation example of go operation etcd

etcdIt is an open-source, distributed key value pair data storage system, which provides shared configuration, service registration and discovery. This paper mainly introduces the installation and use of etcd. Etcdetcd introduction etcdIt is an open source and highly available distributed key value storage system developed with go language, which can be used to configure sharing […]