I have the following function to extract some variables from a html page and publish them via i/o which is working properly - but I want to add a callback to ensure that I can find out if this function has completed fully.
Please advise how i can add a callback purely for this purpose - since I don't have any other need of the callback.
function RunScrapingPositions() {
status = false;
myhttp.get('https://example.com/PB.jsp',
function (_html) {
if (_html && _html.length > 10) {
news.positions = {};
$ = cheerio.load(_html);
$('tr[id^="TR"]').each(function () {
status = true;
var symbol = $('td:nth-child(3)', this).text().trim();
var objob = {
'NQ': parseInt($('td:nth-child(11)', this).text().trim()),
};
var post = {
'symbol': symbol,
'nq': objob.NQ
};
connection.query('INSERT INTO NP SET ?', post, function (err,result){
if (err)
{console.log("NP sql insert error : " +symbol);}
else {
console.log("Posn - Step 3B - Position data inserted into NP Table : " +symbol);
}
});
var objstock = news.analysis[symbol];
if (typeof objstock!='undefined') {
objstock.NQ = objob.NQ;
news.positions[symbol] = objob;
news.analysis[symbol] = objstock;
if (status) {
console.log('Posn - Step 4 - Positions data pushed to page')
io.emit('news', news);
}
}
else
{
console.log('Posn - Step 4A - Position symbol not found');
}
});
if (timerPositions) {
clearTimeout(timerPositions);
}
console.log('Posn - Step 5 - setTimer RunScrapingPositions:' + config.DelayExtractPositions);
timerPositions = setTimeout(RunScrapingPositions, config.DelayExtractPositions);
}
});
}
You could have a callback implemented with the following steps:
Accept a callback function as argument:
function RunScrapingPositions(callbackDone) {
Then, where you call it, pass that function on. You can use bindfor that. And where you have the condition that all is done, call it:
function RunScrapingPositions(callbackDone) {
status = false;
myhttp.get('https://example.com/PB.jsp', function (_html) {
if (_html && _html.length > 10) {
// ...
// pass the callback function on to the deferred call:
timerPositions = setTimeout(
RunScrapingPositions.bind(null, callbackDone),
config.DelayExtractPositions
);
} else {
// all is done, call the callback function:
callbackDone();
}
});
}
You can also use jQuery's custom event facility, $(document).trigger({eventName}, obj or list of arguments), to trigger an "event". You can write code to listen for that event via $(document).on({eventName}, function(someArgsThatYouPass){do something here....}).
Related
I'm creating a var where I want to assign it to a var that I get from another entity
function SubmitAction(executionContext) {
var lookupItem = formContext.getAttribute("alfa_member").getValue()[0].id;
var theTotalMembersTravling = formContext.getAttribute("alfa_numberofdependent").getValue();
var remainFlightCredit;
debugger;
Xrm.WebApi.online.retrieveRecord("contact",lookupItem, "?$select=new_remainstravelcredit").then(
function employessPackage(result) {
var new_remainstravelcredit = result["new_remainstravelcredit"];
if(new_remainstravelcredit !== null){
if(new_remainstravelcredit > 0)
{
remainFlightCredit = new_remainstravelcredit;
console.log(remainFlightCredit+" This not inside any if condition");
var newRemain = (parseInt(remainFlightCredit)) - (parseInt(theTotalMembersTravling));
console.log(newRemain+ " This in the remain if condition");
var entity = {};
entity.new_remainstravelcredit = newRemain.toString();
Xrm.WebApi.online.updateRecord("contact",lookupItem, entity).then(
function success(result) {
var updatedEntityId = result.id;
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);
} if(new_remainstravelcredit <= 0)
{
Xrm.Utility.alertDialog("You have exceeds the travel credit");
console.log(remainFlightCredit);
}
}
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);
console.log(remainFlightCredit);
}
So as result in this line
remainFlightCredit = new_remainstravelcredit;
console.log(remainFlightCredit+" This not inside any if condition");
Which inside the webapi call I'm able to get the value but outside in the main function at the end when I write
console.log(remainFlightCredit);
I'm unable to get the value remainFlightCredit, do you have any suggestions to solve this issue?
This is expected behavior, as this is a promise call (asynchronous) the code outside the main api call will execute before the success callback function employessPackage.
So remainFlightCredit value get assigned after the last console.log line in your code.
You can place a breakpoint to debug and see it in action.
I use the recursive function below, in order to reopen website if httpstatus != 200:
retryOpen = function(){
this.thenOpen("http://www.mywebsite.com", function(response){
utils.dump(response.status);
var httpstatus = response.status;
if(httpstatus != 200){
this.echo("FAILED GET WEBSITE, RETRY");
this.then(retryOpen);
} else{
var thisnow = hello[variable];
this.evaluate(function(valueOptionSelect){
$('select#the_id').val(valueOptionSelect);
$('select#the_id').trigger('change');
},thisnow);
}
});
}
The problem is that sometimes the retryOpen function does not even go as far as to callback function(response){}. Then, my script freezes.
I wonder how one could change the function to be able to recursively try to open website again if there is no response from website (not even some error code as 404 or something)? In other words, how to rewrite the retryOpen function so it reruns when the function does not reach callback after a certain amount of time?
I would try something like this. Please note this is untested code, but should get you on the correct path
retryOpen = function(maxretry){
var count = 0;
function makeCall(url)
{
this.thenOpen(url, function(response){
utils.dump(response.status);
});
}
function openIt(){
makeCall.call(this,"http://www.mywebsite.com");
this.waitFor(function check() {
var res = this.status(false);
return res.currentHTTPStatus === 200;
}, function then() {
var thisnow = hello[variable];
this.evaluate(function(valueOptionSelect){
$('select#the_id').val(valueOptionSelect);
$('select#the_id').trigger('change');
},thisnow);
}, function timeout() { // step to execute if check has failed
if(count < maxretry)
{
openIt.call(this);
}
count++
},
1000 //wait 1 sec
);
}
openIt();
}
I have a problem in my project.
To describe this issue I have wrote simplified code snippet:
function waitFor(fnReady, fnCallback) {
var check = function() {
if (fnReady()) {
fnCallback();
}
else {
setTimeout(check, 100); // wait another 100ms, and try again
}
};
check();
}
var result = 0;
var flag = true;
function ajaxRequest() {
setTimeout(
function() { flag = false;
console.log('ping');
},3000
);
}
function ajaxRequestHandler() {
setTimeout(
function() { flag = true;
console.log('pong');
}, 200
);
}
for(var i =0;i<10; i++){
waitFor(function() { return flag; }, ajaxRequest);
waitFor(function() { return !flag; }, ajaxRequestHandler);
}
it returns:
ping - 10 times
pong - 10 times
desired result:
ping
3 second timeout
ping
---------------------
ping
3 second timeout
pong
--------------------
.....
Can you help correct my code?
UPDATE
Actual problem:
I have a google map.
I have a lot of places when I should to redraw it.
For application logic very important that If I send
request1
request2
request3
request4
I should handle responses in the this order
handle response of request1
handle response of request2
handle response of request3
handle response of request4
Problem that I don't know order of requests.
In different places of file I see following code rows:
google.maps.event.addListener(searchBox, 'bounds_changed', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminals.json', renderTerminalsOnMapAndFitBounds);
.....
$.getJSON('getAllTerminalsInsideRectangle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getAllTerminalsInsideCircle.json', renderTerminalsOnMapAndFitBounds);
...
$.getJSON('getBigTerminals.json', renderTerminalsOnMapAndFitBounds);
........
renderTerminalsOnMapAndFitBounds method sends request to server and in succes alternative render result on map. But this event happens very often
Try this pattern
var map = "abcdefghi".split("");
var responses = []; // collect responses
$.ajaxSetup({
beforeSend : function(jqxhr, settings) {
jqxhr.id = Number(settings.data.split(/id=/)[1]); // add `id` to `request`
console.log(settings.data.split(/id=/)[1]);
}
});
var request = function(id, data) {
// append `id` to `id` data
return $.post("/echo/json/", {json:JSON.stringify([data]), id:id})
};
$.each(map, function(k, v) {
setTimeout(function() {
request(k + 1, v)
.done(function(data) {
// do stuff at each response
console.log(data); // note return values
})
.always(function(data, textStatus, jqxhr) {
// do stuff at each response
responses.push([jqxhr.id, data[0]]);
// do stuff when all requests completed , results items in `responses`
if (responses.length === map.length) {
responses.sort(); // sort `responses` based on `id`
// do stuff with `responses`
console.log(responses);
}
});
},1 + Math.random() * 1000) // async
});
jsfiddle http://jsfiddle.net/guest271314/g254bbjg/
my variant:
var index = 0;
// callback function
function tryMe (param1) {
waitFor(function(){return param1 == index},
function(){console.log(param1);
index++;
}
)
}
// callback executer
function callbackTester (callback,i) {
setTimeout( function(){callback(i);}, 20000 - i*1000);
}
// test function
for(var i=0 ; i<10 ; i++){
callbackTester ( tryMe,i );
}
function waitFor(fnReady, fnCallback) {
var check = function() {
if (fnReady()) {
fnCallback();
}
else {
setTimeout(check, 100); // wait another 100ms, and try again
}
};
check();
}
http://jsfiddle.net/x061dx75/17/
I personally would use promises for this, but you've said no promises (not sure why), so here's a generic sequencer algorithm in plain javascript (tested in the jsFiddle linked below):
function sequence(fn) {
// initialize sequence data upon first use
if (typeof sequence.low === "undefined") {
sequence.low = sequence.high = 0;
sequence.results = {};
}
// save id in local variable so we can reference it in the closure from the function below
var id = sequence.high;
// advance to next sequence number
++sequence.high;
// initialize the result value for this sequence callback
sequence.results[id] = {fn: fn, args: [], ready: false, context: null};
return function(/* args */) {
// save args and context and mark it ready
var args = Array.prototype.slice.call(arguments, 0);
// get the results object for this callback and save info in it
var thisResult = sequence.results[id];
thisResult.args = args;
thisResult.context = this;
thisResult.ready = true;
// now process any requests in order that are ready
for (var i = sequence.low; i < sequence.high; i++) {
var result = sequence.results[i];
// if this one is ready, process it
if (result.ready) {
// increment counter past this result
++sequence.low;
// remove this stored result
delete sequence.results[i];
// process this result
result.fn.apply(result.context, result.args);
} else {
// if this one not ready, then nothing to do yet
break;
}
}
};
}
// your usage:
google.maps.event.addListener(searchBox, 'bounds_changed', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getAllTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
.....
$.getJSON('getAllTerminalsInsideRectangle.json', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getAllTerminalsInsideCircle.json', sequence(renderTerminalsOnMapAndFitBounds));
...
$.getJSON('getBigTerminals.json', sequence(renderTerminalsOnMapAndFitBounds));
........
Working demo: http://jsfiddle.net/jfriend00/aqugm1fs/
Conceptually, what this does is as follows:
Pass a substitute completion handler in place of the normal completion callback.
This substitute function marks each response with a sequence id and saved the original completion handler.
If a response comes back while another response with a lower sequence id is still pending, then the result is just stored and saved for later.
As each response comes in, it processes as many responses in sequence as are ready
Note: while all the examples you have use the same callback function, this will work with any callback function so it would work with a mix of different types of operations.
I m creating mobile web application using html5 and javascript.I m having two javascript files. AttributesDatabase.js and AttributeView.js.From AttributeView.js i m calling one function from AttributeDatabase.js in that i m executing one select query.Now the query result should go to AtttributeView.js.But the Websql transaction is asynchronous call that is what it is not returning proper result.Is there any way to handle the websql result.
Please help if any way there?
Edited
AttributeView.js
var AttributeDAOObj = new AttributeDAO();
AttributeDAOObj.GetAttributeList();
alert(AttributeDAOObj.GetAttributeList()); //This alert is coming as undefined.
AttributeDAO.js
this.GetAttributeList = function () {
var baseDAOObj = new BaseDAO();
var query = "SELECT AttributeName FROM LOGS";
// this.Successcalbackfromsrc = this.myInstance.Successcalback;
var parm = { 'query': query, 'Successcalback': this.myInstance.Successcalback };
baseDAOObj.executeSql(parm);
}
//To Create database and execute sql queries.
function BaseDAO() {
this.myInstance = this;
//Creating database
this.GetMobileWebDB = function () {
if (dbName == null) {
var dbName = 'ABC';
}
var objMobileWebDB = window.openDatabase(dbName, "1.0", dbName, 5 * 1024 * 1024);
return objMobileWebDB;
}
//Executing queries and getting result
this.executeSql = function (query) {
var objMobileWebDB = this.myInstance.GetMobileWebDB();
objMobileWebDB.transaction(function (transaction) {
//In this transaction i m returning the result.The result value is coming.
transaction.executeSql(query, [], function (transaction, result) { return result; }, this.Errorclback);
});
}
}
The problem is in you succes call back (like in the comment to your question, stated by DCoder)
function (transaction, result) { return result; }
this is returning where to?
So this is how to do it (or at least one way)
you can do for example:
function (transaction,result){
console.log("yes, I have some result, but this doesn't say anything, empty result gives also a result");
// so check if there is a result:
if (result != null && result.rows != null) {
if (result.rows.length == 0) {
// do something if there is no result
}else{
for ( var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
var id = result.rows.item(i).id; //supposing there is an id in your result
console.log('Yeah! row id = '+id);
}
}
}else{
// do something if there is no result
}
};
note the code above can be compacter, but this is how to understand it better.
another way is to put this function is a seperate piece of code, so you keep the sql statement more compact and readable. Like you call you error callback this can be in your function (with this. in front of it) or a completely seperate function.
I'm having some trouble with the following javascript code..
var returnValue = false;
function hasItem(id) {
//I want this entire function to run first
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM library WHERE id == "+id,[],function(tx, results) {
returnvalue = results.rows.length>0;
},errorCB);
},errorCB,successCB);
//then this
return returnvalue;
}
But the sql-function appears to run in a separate thread, making the function return false all the time.. is there any way "to force a wait"..?
is there any way "to force a wait"..?
No. What you must do is change your hasItem function so that it accepts a callback that provides the information, instead of returning a value.
It's a bit tricky not knowing what your errorCB and successCB callbacks do, but something along these lines:
function hasItem(id, callback) {
var returnValue = false;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM library WHERE id == "+id,[],function(tx, results) {
returnValue = results.rows.length > 0;
},failed);
},failed,function() {
successCB();
callback(returnValue);
});
function failed() {
errorCB();
callback(null); // Or whatever you want to use to send back the failure
}
}
Then, instead of this
if (hasItem("foo")) {
// Do something knowing it has the item
}
else {
// Do something knowing it doesn't have the item
}
You use it like this:
hasItem("foo", function(flag) {
if (flag) {
// Do something knowing it has the item
}
else {
// Do something knowing it doesn't have the item
// (or the call failed)
}
});
If you want to tell, in the callback, whether the call failed:
hasItem("foo", function(flag) {
if (flag === null) {
// The call failed
}
else if (flag) {
// Do something knowing it has the item
}
else {
// Do something knowing it doesn't have the item
}
});