Ways to convert a function to asynchronous

My experience with building async functions is minimal, and the documentation has left me feeling even more confused.

I am using vue.js as my framework and making calls to firebase in my methods.

Aim

The issue arises when deleting a document in firebase; nested documents are not deleted automatically. To address this, I had to create a custom delete function using recursive functions within a mixin. Although the mixin now works, there is a problem where the reload emit event fires before the delete function completes, leaving a ghost element in the DOM.

I need $emit('reload') to wait until all firebase calls in deleteProject() have finished.

Code Snippet

<script>
import { deleteProject } from '../../../mixins/deleteData'

export default {
  mixins: [deleteProject],
  props: ['yearId', 'projectId', 'project'],
  name: 'deleteProjectModal',
  data: () => ({
    dialog: false
  }),
  methods: {
    del () {
      // call deleteYear method from mixin
      this.deleteProject(this.yearId, this.projectId)
      // emit reload signal for database relaod
      this.$emit('reload')
      // close modal
      this.dialog = false
    }
  }
}
</script>
import firebase from '@/firebase/init'

export const deleteProject = {
  methods: {
    deleteProject (yearId, projectId) {
      // delete project, all documents in the images subcollection of the project, and all stored images
      firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then((doc) => {
          // delete thumbnail
          firebase.storage().ref(doc.data().thumbFullPath).delete()
          // delete project document
          firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).delete()
          firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).collection('images').get()
            .then((querySnapshot) => {
              querySnapshot.forEach((doc) => {
                // delete gallery image
                this.imagePaths.append(doc.data().fullPath)
                // delete reference document for gallery image
                doc.ref.delete()
              })
            })
        })
    }
  }
}

Attempts Made

I believe the solution involves converting deleteProject() into an async function and calling

deleteProject().then($emit('reload'))
. However, I haven't been able to successfully implement this.

Edit

Upon request, here is the function triggered by reload:

import firebase from '@/firebase/init'

export const loadGallery = {
  data: () => ({
    years: []
  }),
  methods: {
    loadProjects () {
      var years = []
      var temp = []
      firebase.firestore().collection('years').orderBy('year', 'desc').onSnapshot((querySnapshot) => {
        querySnapshot.forEach((year) => {
          firebase.firestore().collection('years').doc(year.id).collection('projects').get().then((querySnapshot) => {
            querySnapshot.forEach((project) => {
              var objectMicro = { title: project.data().title, thumbUrl: project.data().thumbUrl, route: project.data().route, id: project.id }
              temp.push(objectMicro)
            })
          }).then(() => {
            var objectMacro = { year: year.data().year, id: year.id, projects: temp }
            years.push(objectMacro)
            temp = []
          })
        })
      })
      this.years = years
    }
  }
}

Answer №1

To ensure smooth execution, remember to include the initial .then() in the Promise sequence within the deleteProject function, allowing subsequent calls to resume from that point.

deleteProject() {
  // Incorporate a return statement here or store the Promise in a variable for proper handling.
  return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then( /* code omitted for brevity */ )
}

In the calling method,

methods: {
  del() {
    this.deleteProject(this.yearId, this.projectId).then(() => {
      this.$emit('reload')
    })
  }
}

Alternatively, enhance readability by utilizing the async keyword as follows:

methods: {
  async del() {
    // It's advisable to include try-catch blocks for error handling.
    await this.deleteProject(this.yearId, this.projectId);

    this.$emit('reload');
  }
}

Answer №2

Multiple asynchronous Firebase operations are being executed in the deleteProject method, including calling Firestore get(), Firestore delete(), Cloud Storage delete(), and more. However, the Promise chaining for these operations is not done correctly.

The Promises returned by these Firebase methods need to be chained using the then() method, which allows for method chaining.

To ensure proper chaining, consider implementing the following code snippet (please note that it's untested):

export const deleteProject = {
   methods: {     
      deleteProject (yearId, projectId) {
      // delete project, all documents in the images subcollection of the project, and all stored images
      return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).get()
        .then((doc) => {
          // delete thumbnail
          return firebase.storage().ref(doc.data().thumbFullPath).delete();
        })
       .then(() => {
          return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).delete();
       })
       .then(() => {
          return firebase.firestore().collection('years').doc(yearId).collection('projects').doc(projectId).collection('images').get();
       })
       .then((querySnapshot) => {
          const promises = [];
          querySnapshot.forEach((doc) => {
            // delete gallery image
            this.imagePaths.append(doc.data().fullPath);
            // delete reference document for gallery image
            promises.push(doc.ref.delete());
          });
          return Promise.all(promises);
       });
      }
  }
}

Since the deleteProject() method is now asynchronous, ensure to call it appropriately within your mixin:

  methods: {
    del () {
      // call deleteYear method from mixin
      this.deleteProject(this.yearId, this.projectId)
      .then(() => {
         // emit reload signal for database relaod
         this.$emit('reload');
         // close modal
         this.dialog = false;
      });
    }
  }

It's important to utilize Promise.all() to execute all the delete() method calls in parallel within the forEach(). The Promise.all() method returns a single Promise that fulfills when all the promises passed as an iterable (i.e., the promises array) have been fulfilled.

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

Momentjs initially indicates that the date is valid, logs the correct date, and displays the accurate duration using .fromNow(). However, this suddenly switches

I am currently working on converting the createdAt date in my Nuxtjs application that is fetched from MongoDB using an express app, with the help of moment.js. Initially, when I check if the date is valid, it shows as valid but then it switches to an incor ...

White border appears when hovering over MUI TextField

I've been troubleshooting this issue for what seems like an eternity. I've combed through Chrome's inspect tool, searching for any hover styles on the fieldset element, but to no avail. Here's my dilemma... I simply want a basic outline ...

What is causing this substring to not function properly in Chinese text?

What could be causing this issue with the substring function when dealing with Chinese text? I have successfully used it for other languages, but when working with Chinese characters, I am getting an empty string as a result. countryID = "奥地利 ...

My Next.js application does not display an error message when the input field is not valid

I've recently developed a currency converter application. It functions properly when a number is provided as input. However, in cases where the user enters anything other than a number or leaves the input field empty, the app fails to respond. I aim t ...

"Encountering an Error with Route.get() when attempting to utilize an imported

I have a function that I exported in index.js and I want to use it in test.js. However, when I try to run node test, I encounter the following error message: Error: Route.get() requires a callback function but got a [object Undefined] What am I doing wro ...

Error: When attempting to overwrite res.render, a TypeError occurs because the property 'app' is being read from an undefined source

I am working on a project where I need to modify the behavior of the res.render method in order to consistently include an additional option, regardless of any other options present. However, when attempting to implement this modification, I encounter th ...

Proper method for incorporating loading and error messages with the help of useContext and react hooks

Currently, I have a context.js file that makes an ajax call and stores the data in an array to be shared across components. While adding some 'loading ...' text during the loading process using axios, I feel there might be a better way to handle ...

Sending an AJAX request from JavaScript to a user control in ASP.NET

JavaScript Function: function CreateProposal(GetProposal, ProductName, ProductID) { $.ajax({ type: "POST", url: "Page.ascx/Proposal", data: JSON.stringify({ GetProposal: GetProposal, ProductName: ProductName, ProductID: ProductID ...

Using the react-bootstrap library, I have successfully integrated a Navbar

The navigation bar below is not functioning properly, and the container is causing the links to lead to a 404 error page. I have attempted writing it in various formats: <Nav.Link href="" >Name</Nav.Link> <Nav.Link href={"&qu ...

Vue simultaneously updating multiple components following a push action

Currently, I am constructing a form using Vue. Included in the form is a component that appears as shown below: <template> <transition name="preview-pane"> <label>{{ option.group }}</label> <in ...

What techniques can I implement with puppeteer to efficiently warm up the cache?

I have a lengthy txt document containing around 1000 URLs that need to be accessed in order to warm up the Varnish cache. Since Puppeteer is required, it's crucial that there is important content loaded through AJAX calls. This is my initial attemp ...

Hook on UI-Router that triggers after the template has been retrieved and added to the DOM

We are faced with the challenge of supporting a legacy module that requires synchronous code to run after templates have been fetched and attached to the DOM. As we transition from our custom router to UI-Router, we have successfully resolved the route an ...

Tips on halting the current process in the update panel and triggering the button click event

Users on my ASP.NET page have the ability to view their customer balance before making a payment. The balance update label is located within an Update Panel, but occasionally there are delays in retrieving this information. In these instances, users may wi ...

What is the reason for Vuetify setting each breakpoint 16px lower than other frameworks, resulting in measurements like 1904 instead of 1920?

My screen has a width of 1920px. When my browser is in fullscreen at 1920px, my vuetify app becomes very wide as it surpasses the 1904px breakpoint. However, I believe that the xl breakpoint should start from 1921 (not 1905) on screens with a width of 1920 ...

Using NicEdit for uploading to your personal server

I'm having trouble uploading images using the NicEdit WYSIWYG editor on my own server. When I click on the upload image button, it redirects me to another site where I can upload the image: imgur dot com You can find a demo of this here: However, I ...

What could be causing the computed property in Vue 2 component to not return the expected state?

I'm encountering an issue with my Vue component where it fails to load due to one of its computed properties being undefined: Error: Cannot read properties of undefined (reading 'map') Here is the snippet of the computed property causing ...

The Vue background container fails to display the image

I'm having trouble setting an image as the background for a div element. Despite my numerous attempts, I haven't been able to make it work. No results are displayed and there are no error messages to indicate what went wrong. I recently stumble ...

How can I pass authentication details using jqGrid?

I am currently working with a service that has CORS support enabled. Typically, when making a server request, I create a request object using jQuery and include the withCredentials parameter set to true, which usually works well. However, I am facing an i ...

Adjust the size of an image and move its position both vertically and horizontally using Javascript

I am currently working on transforming an image both vertically and horizontally, as well as scaling it using JavaScript. While I have managed to resize the image successfully, it doesn't quite look how I want it to. I am aiming for a similar effect a ...

Is there a way to use JavaScript to automatically open a URL at regular intervals?

List of URLs in JavaScript: var websites = ['https://www.google.com', 'https://www.msn.com', 'https://stackoverflow.com']; I have an array containing multiple website URLs. My goal is to open each URL in a new tab or window e ...