indexed_db getObject() - how to return result

2019-02-16 04:44发布

问题:

I would like to know how to define the data type and how to return the object (record) using getObject(). Currently, the only way that I have been able to use the result (record) outside of the function that obtains it is to call another function with the result. That way, the data-type does not need to be specified. However if I want to return the value, I need to define the data-type and I can't find what it is. I tried "dynamic" but that didn't appear to work. For example ":

fDbSelectOneClient(String sKey, Function fSuccess, String sErmes) {
  try {
    idb.Transaction oDbTxn      =   ogDb1.transaction(sgTblClient, 'readwrite');
    idb.ObjectStore oDbTable    =   oDbTxn.objectStore(sgTblClient); 
    idb.Request     oDbReqGet   =   oDbTable.getObject(sKey);
    oDbReqGet.onSuccess.listen((val){
      if (oDbReqGet.result == null) {
        window.alert("Record $sKey was not found - $sErmes");
      } else {
    ///////return oDbReqGet.result;    /// THIS IS WHAT i WANT TO DO
        fSuccess(oDbReqGet.result);    /// THIS IS WHAT i'm HAVING TO DO  
      }});
    oDbReqGet.onError.first.then((e){window.alert(
      "Error reading single Client. Key = $sKey. Error = ${e}");});
  } catch (oError) {
    window.alert("Error attempting to read record for Client $sKey.
       Error = ${oError}");    
  }
}
fAfterAddOrUpdateClient(oDbRec) {
  /// this is one of the functions used as "fSuccess above

回答1:

As someone else once said (can't remember who), once you start using an async API, everything needs to be async.

A typical "Dart" pattern to do this would be to use a Future + Completer pair (although there's nothing inherently wrong with what you've done in your question above - it's more a question of style...).

Conceptually, the fDbSelectOneClient function creates a completer object, and the function returns the completer.future. Then, when the async call completes, you call completer.complete, passing the value in.

A user of the function would call fDbSelectOneClient(...).then((result) => print(result)); to make use of the result in an async way

Your code above could be refactored as follows:

import 'dart:async'; // required for Completer

Future fDbSelectOneClient(String sKey) {
  var completer = new Completer();

  try {
    idb.Transaction oDbTxn      =   ogDb1.transaction(sgTblClient, 'readwrite');
    idb.ObjectStore oDbTable    =   oDbTxn.objectStore(sgTblClient); 
    idb.Request     oDbReqGet   =   oDbTable.getObject(sKey);

    oDbReqGet.onSuccess.listen((val) => completer.complete(oDbReqGet.result));
    oDbReqGet.onError.first.then((err) => completer.completeError(err));
  } 
  catch (oError) {
    completer.completeError(oError);
  }

  return completer.future; // return the future
}

// calling code elsewhere
foo() {
  var key = "Mr Blue";

  fDbSelectOneClient(key)
    .then((result) {
      // do something with result (note, may be null)
    })
    ..catchError((err) {  // note method chaining ..
     // do something with error
    };
}

This future/completer pair only works for one shot (ie, if the onSuccess.listen is called multiple times, then the second time you will get a "Future already completed" error. (I've made an assumption on the basis of the function name fDbSelectOneClient that you are only expecting to select a single record.

To return a value from a single future multiple times, you'll probably have to use the new streams feature of the Future - see here for more details: http://news.dartlang.org/2012/11/introducing-new-streams-api.html

Note also, that Futures and Completers support generics, so you can strongly type the return type as follows:

// strongly typed future
Future<SomeReturnType> fDbSelectOneClient(String sKey) { 
  var completer = new Completer<SomeReturnType>(); 

  completer.complete(new SomeReturnType());
}

foo() {
  // strongly typed result
  fDbSelectOneClient("Mr Blue").then((SomeReturnType result) => print(result));
}


标签: dart