Function with defer that calls itself repeatedly

I am attempting to utilize a recursive function where each function runs only after the previous one has completed. This is the code I have written:

var service = ['users', 'news'],
    lastSync = {
                 'users' : false,
                 'news' : false
               };
db.transaction(function (tx) {
   lastSyncFunc(tx,service,0).then(function(){
       console.log(lastSync);
   });
});

function lastSyncFunc(tx,service,index){
   deferred = $q.defer();
   tx.executeSql("SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", [service[index]], function (tx, result) {
       if (result.rows.length > 0) {
           lastSync[service[index]] = result.rows.item(0).fTime;
       }
       return ++index<service.length ? lastSyncFunc(tx,service,index) : deferred.resolve();
   });
   return deferred.promise;
}

Currently, my program is returning false for both lastSync.users and lastSync.users because this section is being executed before the function completes.

Answer №1

It may not always be the best decision to handle manual multiple asynchronous calls.
Consider using $q.all() for a more efficient approach.

Simplify the process by first creating a promisified version for a single query:

const pDbExec = (tx, sql, params = []) => {
  let deferred = $q.defer();
  tx.executeSql(sql, params, (tx, res) => deferred.resolve(res));
  return deferred.promise();
}

The initial step should involve checking if there is already a promisified version available for the library/methods being used.

Then, proceed to call $q.all while mapping your service list into promises:

const SQL_SvcLastSync = `SELECT time FROM ... LIMIT 1`;
db.transaction(tx => {
  $q.all(service.map(svc => pDbExec(tx, SQL_SvcLastSync, [svc])))
    .then(results => 
       results.map(res => 
         res.rows.length > 0 ? res.rows.item(0).fTime : null))
    .then(results => console.log(results));
});

To format results as key/value pairs, you have two options:

  • Add a reducer:
    .then(results => 
      results.reduce((acc, res, i) => (acc[service[i]]=res, acc), {}))
  • Provide key/value pairs (where values are promises) instead of an array map to $q.all, so they will be resolved under the same keys.
    Note that you may need to modify the intermediate mapper in this case.

A simple solution involves adding a parameter to save the same deferred object between recursive calls:

You can try something like this:

function lastSyncFunc(tx, service, index, def){
   var deferred = def || $q.defer();
   tx.executeSql(
     "SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", 
     [service[index]], 
     function (tx, result) {
       if (result.rows.length > 0) {
           lastSync[service[index]] = result.rows.item(0).fTime;
       }
       return ++index<service.length ? 
         lastSyncFunc(tx, service, index, deferred) : 
         deferred.resolve();
   });
   return deferred.promise;
}

This method provides a deferred object to the maximum depth, allowing it to be resolved appropriately.

Answer №2

If you want to accomplish this task efficiently, it is recommended to steer clear of recursion as explained in detail here under "The Collection Kerfuffle" section.

The suggested approach involves organizing everything within a db.transaction(function() {...}) framework, but the code becomes more comprehensible if you extract tx.executeSql(...) and convert it into a separate function with promises.

After implementing these changes, your code will resemble something along these lines:

var service = ['users', 'news'],
    lastSync = {
        'users' : false,
        'news' : false
    };
db.transaction(function (tx) {
    return service.reduce(function(promise, serviceItem) {
        return promise.then(function() {
            return executeSqlPromisified(tx, serviceItem).then(function(fTime) {
                if(fTime !== null) {
                    lastSync[serviceItem] = fTime;
                }
            });
        });
    }, $q.when(null)).then(function() {
        console.log(lastSync);
    });
});

function executeSqlPromisified(tx, serviceItem) {
    var deferred = $q.defer();
    tx.executeSql("SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", [serviceItem], function (tx, result) {
        if (result.rows.length) {
            deferred.resolve(result.rows.item(0).fTime);
        } else {
            deferred.resolve(null);
        }
    });
    return deferred.promise;
}

Initially, this format may seem overwhelming, but once you familiarize yourself with this pattern, navigating through the code will become easier.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

``Error encountered when attempting to navigate AngularJS route with trailing forward slash

I have a link that is working perfectly fine, like this: when('/videos', { templateUrl: 'partials/partial1', controller: 'MyCtrl1' }). But when I add a named group to the route like this: when('/videos/:video_id&ap ...

Oops! Make sure to explicitly allow the dependency @types/html2canvas by adding it to the "allowedNonPeerDependencies" option

After installing the html2canvas package in my Angular library project, I encountered an error when compiling in production mode using the command ng build --prod. The specific error message is as follows: ERROR: Dependency @types/html2canvas must be exp ...

How can I incorporate JSON data retrieved from my backend server into the content of Tabs in a ReactJS application, utilizing either the map() or forEach() method

I need help assigning a key from an object to each tab using a loop in order to navigate to its content when clicked on. I attempted to use map() but it didn't work, so I'm looking for guidance as I am new to React. Below is the code. Any assist ...

Steer clear of downloading images while on your smartphone

As I develop a responsive website, I encounter the challenge of optimizing image loading based on viewport size. While using CSS to hide images not needed for certain viewports can reduce display clutter, it does not prevent those images from being downloa ...

Using AJAX, SQL and PHP to send data to a separate page for processing

This is the code I use to retrieve questions via ajax from a list of questions stored in a SQL database. <form id="reg-form3"> <ul class="nav nav-list primary push-bottom"> <? $db['db_host']="localhost"; ...

Utilizing JSON API filtering within a Next.js application

Recently delving into the world of coding, I've embarked on a personal project that has presented me with a bit of a challenge regarding API filtering. My goal is to render data only if it contains a specific word, like "known_for_department==Directin ...

Steps for Adding a JSON Array into an Object in Angular

Here is a JSON Array that I have: 0: {name: "Jan", value: 12} 1: {name: "Mar", value: 14} 2: {name: "Feb", value: 11} 3: {name: "Apr", value: 10} 4: {name: "May", value: 14} 5: {name: "Jun", value ...

The React Query devtools are failing to display

I am currently working on a project using React Query, but for some reason, the DevTools icon is not appearing on my screen. I have checked the console for errors, but there are none. I am following a tutorial on YouTube to help me with this. Here is a sn ...

Revise Script to Duplicate Alt Attribute onto Miniatures

I'm customizing a gallery plugin for a website that I am currently developing, aiming to add support for titles. The script I am using can be found here: DEMO: http://jquery.malsup.com/cycle/pager2.html CODE: All the functionalities are in place e ...

How to dynamically adjust font size using Javascript

Is there a way to adjust the font size of the left-column using JavaScript? <div id="left-column"> <a href="">1</a><br> <a href="">2</a><br> <a href="">3</a><br> <a href=""> ...

Execute the function only in response to changes in the data

I have a scenario where I am making an ajax call every 3 seconds to keep my app updated with rapidly changing information. However, the expensive function that I run inside the $.done() callback is causing my app to slow down. I want to optimize this proce ...

Is there a way to retrieve the Marker that is being dragged when the event handler has already been bound?

How can I identify the Marker that is being dragged when the handler is a bound function? Take a look at this snippet from my react component: constructor() { this.handleMarkerMove = this.handleMarkerMove.bind(this); } createMarker() { const marker ...

Is it possible to use Vuelidate for password validation in Vue.js?

I found a helpful reference on How to validate password with Vuelidate? validations: { user: { password: { required, containsUppercase: function(value) { return /[A-Z]/.test(value) }, containsLowercase: fu ...

How can Selenium in Python be used to click a JavaScript button?

I need help automating the click of a button on a webpage using selenium Here is the HTML for the button: <div class="wdpv_vote_up "> <input value="7787" type="hidden"> <input class="wdpv_blog_id" value="1" type="hidden"> </div& ...

Quantifying the passage of time for data entry

Looking to create a quiz form where I can track how long it takes users to input/select answers. I attempted the following code but the timing seems off: $('input, select').on('focus', function(event) { el = $(this); name ...

Bower fails to add components during installation

For my current project, I have been utilizing bower locally to handle JavaScript dependencies with no issues. However, today I encountered a problem when attempting to initiate a new project by reusing code from previous projects. Specifically, I tried to ...

AngularJs promise is not resolved

Before processing any request, I always verify the user's authorization. Here is the factory code that handles this: (function() { angular.module('employeeApp').factory('authenticationFactory', authenticationFactory); fun ...

Exploring the comparison of information across two sets of databases using Node.js and MongoDB

In the realm of Node.js and MongoDB, I have set up two databases (1-btech2014 & 2-btechre2014) on a single server. My objective is to compare the data in btech2014 with that in btechre2014. If they match, I should retrieve the data from btech2014 as outpu ...

Performing a GET call within the configuration settings of AngularJS

I found a similar question here but unfortunately it didn't solve my issue. I am currently using Angular-UI's Google Maps API, which requires configuration as follows: .config(function(uiGmapGoogleMapApiProvider) { uiGmapGoogleMapApiProvider ...

I am currently working on completing my order book code, but I am struggling to understand how to summarize the objects

I have been working on my order book code and I am struggling to figure out how to summarize objects. Below is the current code that I have: const orderbook = [ { "ClientID": "31135d2c-a5f0-11ed-b07a-10e7c6f7c62e", "Side": "BID", ...