javascriptreactjsreact-nativereact-native-sqlite-storage

How to share a variable between async functions?


I'm new to React-Native and I was confused on how to have a variable that can be accessed by all the functions within a file without being in a class.

My issues is that I assigned storage in taskFour() and I want that value to be returned in runDemo() but for some reason when I console.log(storage) in runDemo() it returns undefined!

I have defined a helper file with a bunch of functions that call upon each other.

Helper.js

import React from 'react';
import SQLite from 'react-native-sqlite-storage';

let db;
let storage;

function runDemo() {
  loadAndQueryDB();
 //Suppose to return value assigned in queryPeopleSuccess but console logs 'undefined'
  console.log(storage);
  return storage;
}

//    Sends an update saying that Database was successfully opened
function openCB() {
  console.log("Success Opening DB");
}

//      Sends an update with error message and returns FALSE
function errorCB(err) {
  console.log("SQL Error: ", err);
  return false;
}

/** 2. Called when runDemo is called **/
/*   assigns variable 'db' to opened Database */
/*   Calls queryPeople(db) */
function loadAndQueryDB() {
  console.log("Opening Database...: ");
  
  db = SQLite.openDatabase({ name: "users.db", createFromLocation: 1}, openCB, errorCB);
  queryPeople(db);
}

/** 3. Called when loadAndQueryDB is called **/
/*    Get the DB and applies a SQL call that if successful will call queryPeopleSuccess*/
function queryPeople(db) {
  console.log("Executing employee query...");
  //Execute a database transaction.
  db.transaction((tx) => {
    tx.executeSql('SELECT * FROM users', [], queryPeopleSuccess, errorCB);
  });

}

function queryPeopleSuccess(tx, results) {
  var len = results.rows.length;
  let localArray = [];
  //Go through each item in dataset
  for (let i = 0; i < len; i++) {
    let row = results.rows.item(i);
    localArray.push(row);
  }

  storage = localArray;
}

export {
  runDemo,
}

I thought by assigning "storage" outside of the functions would make it a variable accessible to all the functions in this file without making it a global variable. In addition, one restriction I have is that I can't return storage from queryPeopleSuccess all the way back to runDemo due to functions within these functions that are not suppose to have a return value!

Could someone please point out how I can have a variable within a file that does not need to be in a class that can be accessed and edited by the functions within that file?

EDITED: Edited for clarity and typos. Turns out the issue with my code is due to async!


Solution

  • Your problem is that runDemo has long returned before queryPeopleSuccess is even writing to storage. So, you are reading it before it gets written, and since information can't travel back in time, you get undefined. Basically you have this problem.

    You could greatly simplify your code by using an asynchronous function instead:

    import SQLite from 'react-native-sqlite-storage'
    
    export async function runDemo () {
      console.log('Opening Database...: ')
    
      try {
        // This one is tricky, because it seems the library synchronously
        // returns the db instance, but before that it calls the success or
        // error callbacks (without passing the instance).
        const db = await new Promise((resolve, reject) => {
          const db = SQLite.openDatabase(
            { name: 'users.db', createFromLocation: 1 },
            // The trick here is that in the success case we wait for openDatabase
            // to return so that db will be assigned and we can resolve the promise
            // with it.
            () => setTimeout(() => resolve(db), 0),
            reject
          )
        })
        console.log('Success Opening DB')
    
        console.log('Executing employee query...')
        // Execute a database transaction.
        const results = await new Promise((resolve, reject) => {
          db.transaction(tx => {
            tx.executeSql(
              'SELECT * FROM users',
              [],
              (tx, results) => resolve(results),
              reject
            )
          })
        })
    
        const localArray = []
        // Go through each item in dataset
        for (let i = 0; i < results.rows.length; i++) {
          localArray.push(results.rows.item(i))
        }
        return localArray
      } catch (e) {
        console.log('SQL Error: ', e)
        return false
      }
    }
    

    Then you would call runDemo as console.log(await runDemo()) inside another async function, or runDemo().then(results => console.log(results), err => console.error(err)) otherwise.