How to Get an Ajax Get-Request to Wait for the Page to Be Rendered Before Returning a Response

How to get an AJAX get-request to wait for the page to be rendered before returning a response?

In order for the AJAX get to "wait for the page to be rendered", it would actually have to fully process the page, fetching and running all the included CSS and javascript files. That's not easy and not recommended. Fortunately, you don't need to do that anyway.

Here are three better ways to approach this kind of problem:

  1. The resource page (mpdining.rewardsnetwork.com, for this question) might have an API. If it does, find it and use it. This is your best bet, if it's available.

  2. Analyze the resource page's javascript and/or AJAX requests. Use GM_xmlhttpRequest() to directly fetch just the payload data, instead of trying to parse the resource page.

    Sometimes this process is fairly easy, but some sites require complex interaction and/or authentication.

  3. Load the resource page in a hidden iframe; set your Greasemonkey script to run on both the resource page and the master page and to relay the desired data using postMessage().

    This approach will almost always work, although you may have to prevent some pages from attempting to "bust out" of the iframe.



Using a hidden iframe to get data from a cross-domain, resource page:

Greasemonkey scripts will run on both a normal page and on pages within iframes. In fact, you can set the same script to run on both, and on multiple domains.

If a master page and an iframed resource page are both running GM script(s), the script instances can communicate with each other, cross-domain, using postMessage().

For example, suppose we have a site, fiddle.jshell.net/9ttvF/show, that contains travel data, and we want to mash-up that site with matching data from a resource site, jsbin.com/ahacab, that uses AJAX to get its payload data.

The target (master) site looks like this:

target site

The resource site looks like this at first:

resource site, start

Then finishes like this:
resource site, finish


The following script:

  1. Loads the resource page in a hidden iframe.
  2. Starts a second instance of itself running on the iframed page.
  3. Waits for the iframed page to finish, processing the results as desired.
  4. Sends the desired payload data to the GM script running on the target (master) page.
  5. The target-page's script then inserts the payload data to complete the mash-up.
// ==UserScript==
// @name _Cross-site, AJAX scrape demo
// @include http://fiddle.jshell.net/9ttvF/show/
// @include http://jsbin.com/ahacab*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==

if (/fiddle\.jshell\.net/i.test (location.host) ) {
console.log ("***Master-page start...");

/*--- Inform the user.
*/
$("#plainResults").before (
'<div id="gmFetchRez">Greasemonkey is fetching results from jsbin.com...</div>'
);

/*--- Setup to process messages from the GM instance running on the iFrame:
*/
window.addEventListener ("message", receiveMessage, false);

/*--- Load the resource site in a hidden iframe.
*/
$("body").append ('<iframe src="http://jsbin.com/ahacab" id="gmIframe"></iframe>');
}
else {
console.log ("***Framed start...");
/*--- Wait for the AJAXed-in content...
*/
waitForKeyElements ("#results table.rezTable", sendResourcePageData);
}

function sendResourcePageData (jNode) {
console.log ("Results found! Sending them to the main window...");

window.top.postMessage (jNode.html(), "*");
}

function receiveMessage (event) {
if (event.origin != "http://jsbin.com") return;

$("#gmFetchRez").html (event.data);
}

//--- Use CSS to control appearances.
GM_addStyle ( " \
#gmIframe { \
display: none; \
} \
#gmFetchRez { \
background: lightYellow; \
border: 3px double red; \
padding: 1em; \
} \
" );

The final result looks like this, with the script installed and running:
mashup result

How to wait for AJAX response and only after that render the component?

Here's your code reworked with some comments of the modified parts

  getInitialState: function(){
return {
// [{}] is weird, either use undefined (or [] but undefined is better).
// If you use [], you loose the information of a "pending" request, as
// you won't be able to make a distinction between a pending request,
// and a response that returns an empty array
response: undefined
}
},

  loadSubscriptionData: function(subscriptionId){
var self = this;

// You probably want to redo the ajax request only when the
// subscriptionId has changed (I guess?)
var subscriptionIdHasChanged =
(this.props.subscriptionId !== subscriptionId)

if ( subscriptionIdHasChanged ) {
// Not required, but can be useful if you want to provide the user
// immediate feedback and erase the existing content before
// the new response has been loaded
this.setState({response: undefined});

$.ajax({
type: 'GET',
url: '/subscription',
data: {
subscriptionId: subscriptionId //1
},
success: function(data){

// Prevent concurrency issues if subscriptionId changes often:
// you are only interested in the results of the last ajax
// request that was made.
if ( self.props.subscriptionId === subscriptionId ) {
self.setState({
response: data
});
}
}
});
}
},

  // You want to load subscriptions not only when the component update but also when it gets mounted. 
componentDidMount: function(){
this.loadSubscriptionData(this.props.subscriptionId);
},
componentWillReceiveProps: function(nextProps){
this.loadSubscriptionData(nextProps.subscriptionId);
},

  render: function(){

// Handle case where the response is not here yet
if ( !this.state.response ) {
// Note that you can return false it you want nothing to be put in the dom
// This is also your chance to render a spinner or something...
return <div>The responsive it not here yet!</div>
}

// Gives you the opportunity to handle the case where the ajax request
// completed but the result array is empty
if ( this.state.response.length === 0 ) {
return <div>No result found for this subscription</div>;
}

// Normal case
var listSubscriptions = this.state.response.map(function(index){
return (
index.id
)
});
return (
<div>
{listSubscriptions}
</div>
)
}

How do I make jQuery wait for an Ajax call to finish before it returns?

If you don't want the $.ajax() function to return immediately, set the async option to false:

$(".my_link").click(
function(){
$.ajax({
url: $(this).attr('href'),
type: 'GET',
async: false,
cache: false,
timeout: 30000,
fail: function(){
return true;
},
done: function(msg){
if (parseFloat(msg)){
return false;
} else {
return true;
}
}
});
});

But, I would note that this would be counter to the point of AJAX. Also, you should be handling the response in the fail and done functions. Those functions will only be called when the response is received from the server.

How to hold page render until an AJAX call completes?

Update 03/2015: I would strongly advise against using this method, since synchronous XMLHttpRequests on the main thread are now deprecated; see http://xhr.spec.whatwg.org/

Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user's experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when the JavaScript global environment is a document environment. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an InvalidAccessError exception when it occurs.


jQuery.ajax() sports an async parameter. When set to false, your AJAX request becomes synchronous / blocking.

As for the page load: Do not hook into any "ready" event. Just call directly after loading the jQuery library:

$('body').css('display', 'none');

Then run your (now synchronous) AJAX request.

On AJAX completion/success, Do

$('body').css('display', 'block');

Cheers.

Get component to wait for asynchronous data before rendering

In cases like there's asynchronous loading of data, we typically use a simple v-if to hide the element until the data is present.

The template would be like:

<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>

Notice the use of this in the template is unnecessary.

Also, in your case, instead of the beforeMount() hook, you would add a (deep/immediate) watch to the prop, to pick up changes when it is loaded externally:

  watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},

Full demo below.

Vue.component('questionarea', {
props: ["scenariotype"],
data: function () {
return ({
position: "",
vehicleType: ""
});
},
methods: {
transformValuesForDisplay: function () {
switch (this.scenariotype.perspective) {
case 1: {
this.position = "Driver";
this.vehicleType = "Autonomous";
break;
}
case 2: {
this.position = "Passenger";
this.vehicleType = "Manually Driven";
break;
}
case 3: {
this.position = "Driver";
this.vehicleType = "Manually Driven";
break;
}
}
}
},
watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},
template:
`<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>`
});

new Vue({
el: '#app',
data: {
ScenarioType: {perspective: null}
},
methods: {
checkScenarioType: function () {
this.$http.get('https://reqres.in/api/users/2').then(response => {
// get body data
this.ScenarioType.perspective = response.body.data.id; // for testing purposes only
}, response => {
// error callback
});
}
},
mounted: function() {
this.checkScenarioType();
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-resource"></script>

<div id="app">
<p>Notice while it is null, the h1 is hidden: {{ ScenarioType }}</p>
<br>
<questionarea :scenariotype="ScenarioType"></questionarea>
</div>

Delay the loading of a page until AJAX calls received

Ajax calls are asynchronous. So the rest of the page will always execute while the call is being done. I'm not exactly sure what you are trying to accomplish, but if you wish to show the value of Bitcoin on the input box and then let users change it after it's been loaded, you could follow something like:

  • Start with input box disabled;
  • Load bitcoin with AJAX call;
  • Set the response price into the field;
  • Enable the field so users can edit;

That could look like:
(I'm removing part of the code to exemplify, but if you have any questions let me know)

  <head>
<script>
var bpv;
document.addEventListener("DOMContentLoaded", function(event) {
getPrice();
});

function getPrice(){
$.ajax({
dataType:"json",
type: 'GET',
url:'https://blockchain.info/ticker',
success: function(data){
bpv = data.USD.last;
setPriceInput(bpv);
}
});
}

function setPriceInput(value) {
var input = document.getElementById('bp');
input.value = value;
input.disabled = false;
}
</script>
</head>
<body>
Value of Bitcoin ($)<br/>
<input type="text" id="bp" value="" disabled>
</body>

https://jsfiddle.net/81oqsk5x/1/

This solution will wait for the page to be rendered, then try to get the price. Once the price is responded from the API, it will set the price on the input and enable it again (if you look at the input element, it starts as disabled)



Related Topics



Leave a reply



Submit