synchronously retrieve the result of an asynchronous function

In my current project, I am developing a custom render function for marked. This function is responsible for checking specific conditions on images, performing an asynchronous request, and ultimately returning an image from a different source. The challenge lies in the fact that the async request results in the return of a promise rather than a direct image URL.

The attachmentService.getBlobUrl function plays a crucial role in this process. It is an asynchronous function that makes an HTTP request and returns a promise.

Here is a snippet of my render function implementation:

marked.use({
    renderer: {
        image: (src, title, text) => {
            if (someCondition) {

                // Parsing logic for the src attribute
                // ...

                return this.attachmentService.getBlobUrl(attachment)
                    .then(url => {
                        return Promise.resolve(`<img src="${url}" alt="${text}" title="${title}"/>`)
                    })
            }

            return `<img src="${src}" alt="${text}" title="${title}"/>`
        },
    }
})

I have attempted different approaches to handle the promise returned by the attachmentService.getBlobUrl function. One option was to directly return the image tag within the promise chain:

// ...
return this.attachmentService.getBlobUrl(attachment)
    .then(url => {
        return `<img src="${url}" alt="${text}" title="${title}"/>`
    })
// ...

An alternative attempt involved utilizing an async wrapper without using Promise.resolve:

// ...
return (async () => {
    return await this.attachmentService.getBlobUrl(attachment)
        .then(url => {
            return `<img src="${url}" alt="${text}" title="${title}"/>`
        })
})()
// ...

Both methods led to the same outcome - obtaining a promise instead of the desired result.

It is essential to note that using await is not viable in this scenario due to the requirement for the render function to remain synchronous, which is beyond my control.

Answer №1

If you want to delay your asynchronous task:

  1. To customize rendering, simply assign a unique class name to the img elements that require special treatment. You can also modify the src attribute to display a loading image.
  2. Next, before these elements are rendered, use a MutationObserver to monitor the addition of these specific elements. When the MutationObserver detects new elements, it triggers an asynchronous operation and updates the element's src.

Answer №2

It's crucial that the render function operates synchronously.

Trying to incorporate an asynchronous function like getBlobUrl into it is not feasible. There is no way to make it work, it's simply impossible.

Instead, you'll have to reconsider your strategy. Handle the async requests and conditions prior to calling marked. Then provide the data that can be rendered synchronously.

Answer №3

In the process, I decided to introduce a new class for the img elements that required special handling. I then looped over these elements after converting markdown to HTML:

marked.use({
    renderer: {
        image: (src, title, text) => {

            title = title ? ` title="${title}` : ''

            if (someCondition) {
                return `<img data-src="${src}" alt="${text}" ${title} class="attachment-image"/>`
            }

            return `<img src="${src}" alt="${text}" ${title}/>`
        },
    }
})

this.preview = DOMPurify.sanitize(marked(this.text))

// As the render function is synchronous, async http requests cannot be made within it.
// Thus, resolving the blob URL at compile time during markdown processing is not possible.
// To address this limitation, we adjust the URL after rendering in the Vue component.
// This processing occurs in the next tick to ensure that the image elements are
// present in the DOM tree. If executed immediately after setting this.preview,
// there's a chance the images may already be available.
this.$nextTick(() => {
    document.getElementsByClassName('attachment-image').forEach(img => {

        // ...
        // some code ...
        // ...

        this.attachmentService.getBlobUrl(attachment)
            .then(url => {
                img.src = url
            })
    })
})

This scenario takes place within a Vue.js component where this.preview rendered as v-html on an element.

The use of this.$nextTick is essential to guarantee the presence of img elements before making any modifications.

While similar to @shilch's suggestion, this approach aligns more with Vue.js practices.

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

Creating a universal property class for all components in a VueJS project: step-by-step guide

In my VueJS project, I am utilizing Vuetify and have noticed that several components display text in uppercase. To override this, I need to add the class="text-none" property to each component individually. Is there a way to apply this attribute ...

Is it possible for JavaScript to access and read a local file from a web page hosted locally

I am interested in using html, css, and javascript to develop a user interface for configuring a simulation. This interface will be used to generate a visualization of the simulation and an output parameters file based on multiple input files. It is impor ...

The GIF Loader fails to animate while an AJAX request is in progress

Displaying a DIV element containing a loading GIF image while waiting for an AJAX call response. Initially, the DIV element is hidden. Upon clicking a checkbox, the loader DIV is shown, followed by the completion of the AJAX call, and then hiding the load ...

Ordering Operations in Redux

Right now, I'm facing a situation where I need Redux Actions to run one after the other in sequence. I've come across different middlewares like redux-promise that work well if you know the successive actions when triggering the root action. My ...

HTML: Efficiently updating multiple cell contents in a large table using jQuery or JavaScript

Hello, I am currently working on developing an HTML page that consists of a large data table. My goal is to have the data in the table update dynamically as the user interacts with various controls on the page, without having to reload the entire page. ...

The functionality of the slick slider seems to be malfunctioning

I've been trying to integrate the slick slider into my website, but it just won't work. I've double-checked everything multiple times, but I can't seem to find the issue. It's driving me crazy! If anyone can help me identify the pr ...

The Laravel controller is unable to store files sent from an ajax request

Having trouble uploading image files to a controller via ajax. My partner is supposed to have multiple images. I managed to send my images to the controller via ajax, but they are not being stored in the database for some reason. When I try to output the r ...

"Encountering a Problem with Assigning Variables in Vue

My issue revolves around the integration of VueJs, Vue-Resource, and Laravel. The problem occurs when attempting to assign a local variable to response data received from an AJAX request using vue-resource. Code Javascript <script> flags_ ...

Exploring the inner workings of an Object by accessing its array

In my functional component, I'm rendering the values of an object. const MovieItemDetails = (props) => { return <div className='item-details'> <div> <img key={props.movieDetails.id} src={`https://image.tmdb ...

Converting my HTML navigation menu to PHP

In the past, this is what I had: <li><a class="selected0" href="/dbp/dbma.html">Dashboard 0</a></li> <!-- First dashboard selection --> <li><a class="select1" href="#" onclick="r ...

Continuously flowing chain of replies from a series of queries using RxJS

I am exploring the world of RxJS and seeking guidance from experienced individuals. My goal is to establish a synchronized flow of responses, along with their corresponding requests, from a stream of payload data. The desired approach involves sending ea ...

Unable to store loop information fetched from api into a JSON file

I'm currently facing an issue with my code where I am trying to save the results from looping through an API into a JSON file using writeFileSync. Here is the code snippet in question: function processRarity(limit) { var resultJson = []; for ( ...

Managing JSON data retrieval and manipulation techniques

My code is set up to display the image, title, and summary for all entries in a JSON file. However, I only want to display the image, title, and summary for the first entry, and only show the title for the rest of the entries. Please advise. <html> ...

Utilizing V-model and Value attributes simultaneously in a VueJS element

I am trying to incorporate both V-model and Value in the same input field. Specifically, I need to use Value to set the initial value from a relationship in Laravel, while using V-model for real-time data binding in the input field. My research indicated ...

A guide on applying bold formatting to a specific section of text in React

I have a collection of phrases structured like so: [ { text: "This is a sentence." boldSubstrings: [ { offset: 5, length: 2 } ] } ] My goal is to display each phrase as a line using the following format: ...

Tips for effectively sharing content on social media from your Vuejs application

I have been using the vue-social-sharing library to enable social media sharing on my website, and overall it's been working well. However, I am facing a problem where when I click the Facebook share button, it doesn't share the title, descriptio ...

Discovering the dimensions of an image using jQuery on various browsers like Chrome and Safari

I am new to jQuery and I am currently trying to retrieve the height of an image using jQuery. Here's the code I have written: $(document).ready(function ($) { var height = $('#testor').height(); $('.slider-container').css("h ...

Network displays response body but not console output

After sending a request to a link, I noticed that the response logged in the console (console.log(res) and console.log(res.body)) displayed a ReadableStream with no content inside. The value of bodyUsed is also set to false, which is puzzling me at the mo ...

Firefox having trouble loading ThreeJS

My current project involves showcasing 3D files online using ThreeJS. While it operates smoothly on Chrome and MS Edge, I encountered a hitch on Firefox: Uncaught TypeError: Error resolving module specifier “three”. Relative module specifiers must star ...

Utilizing Vue and Django: The best method for distinguishing publicPath from static file prefix

The process of transforming my extensive Django project, which currently integrates Vue from a CDN on individual frontend pages, into a Single Page Application (SPA) using NPM has presented some challenges. The backend and frontend are now separate entitie ...