             transaction = WriteTransaction(index: self)
         }
     }
-    
-    public func commitTransaction(completionHandler: @escaping CompletionHandler) {
+
+    @discardableResult
+    public func commitTransaction(completionHandler: @escaping CompletionHandler) -> Operation {
         assert(transaction != nil, "No transaction is open")
-        transaction!.commit(completionHandler: completionHandler)
-        transaction = nil
+        let operation = BlockOperation() {
+            let transaction = self.transaction!
+            self.transaction = nil
+            do {
+                try transaction.commit()
+                self.callCompletionHandler(completionHandler, content: [:], error: nil)
+            } catch let e {
+                self.callCompletionHandler(completionHandler, content: nil, error: e)
+            }
+        }
+        transactionQueue.addOperation(operation)
+        return operation
+    }
+    
+    public func commitTransactionSync() throws {
+        assert(!Thread.isMainThread, "Synchronous methods should not be called from the main thread")
+        try transaction!.commit()
     }
     
     public func rollbackTransaction() {

        /// Temporary in-memory cache for objects.
        private var tmpObjects: [JSONObject] = []
        
        // MARK: Constants
        
        /// Maximum number of objects to keep in memory before flushing to disk.

        /// - parameter objectID: Identifier of the object to delete.
        ///
        func deleteObject(withID objectID: String) throws {
            self_lock.sync {
                assert(!finished)
                deletedObjectIDs.insert(objectID)
            }
        }
        
        /// Delete several objects.
