I am trying to achieve the asynchronous function calls for reading the odata in SAPUI5.
here is a example:
this.odataModel = new sap.ui.model.odata.ODataModel(oDataUrl, true, 'user', 'pwd');
/* data retrieving time 5sec */
function read1(){
this.odataModel.read('entity-url1',null, null, true, function(data){
console.log('success');
return data;
},
function(){ console.log('error'); });
}
/* data retrieving time 10sec */
function read2(){
this.odataModel.read('entity-url2',null, null, true, function(data){
console.log('success');
return data;
},
function(){ console.log('error'); });
}
/* data retrieving time 10sec */
function read3(){
this.odataModel.read('entity-ur3',null, null, true, function(data){
console.log('success');
return data;
},
function(){ console.log('error'); });
}
/* function calls */
var d1 = read1();
var d2 = read2();
var d3 = read3();
after this call i am trying to get the data from read3 function i am getting empty data. becuse of execution time.
How can i wait for until execution is completed.
You won't see any data because your requests are performed asynchronously! If you want to process the data you have to do this in the success callback of the model's read method. But even with a synchronous call you won't see data, as your methods read1, read2, read3 do not return the data.
You're seeing this because the sap.ui.model.odata.ODataModel.read() calls are asynchronous by default - while the read in your read1() call is being executed, the Javascript event queue is continuing processing and executing the remainder of your code. This asynchronous execution is a Good Thing, but you need to cater for it in your code.
There are a few solutions, but the easiest is to only initiate the second read once the first has executed and only initiate the third once the second has completed. You can do this by modifying your success handlers.
So, a very crude adjustment to read1():
function read1(){
this.odataModel.read('entity-url1',null, null, true, function(data){
console.log('success');
read2();
return data;
},
function(){ console.log('error'); });
}
Then, do the same for read2() and modify your caller to only call read1(). Of course, you'll need to adjust how you return the data to the caller.
A bit untidy, but I hope the above shows the gist of what I'm trying to describe.
You can use jQuery or native promise
//jQuery version
function read1() {
var promise = jQuery.Deferred();
this.odataModel.read('entity-url1', null, null, true, function(data) {
console.log('success');
promise.resolve(data);
},
function() {
console.log('error');
});
return promise;
}
var d1 = read1(); // d1 promise;
d1.then(function(data) {
console.log(data);
});
Related
I am creating a function to read different JSON files. The problem is when I try to pass the array.
I keep getting 'undefined' once I am back to my primary function.
Reading the file works but when I try to use the variable I get 'undefined'.
I could use some help. thanks.
This is the file I read 'data.json':
[
{
"code":"10000",
"name":"new",
"filter":"Office",
"label":"NEW"
},
{
"code":"10001",
"name":"classic",
"filter":"Office",
"label":"CLASSIC"
},
{
"code":"10002",
"name":"old",
"filter":"Office",
"label":"OLD"
}
]
Here's my code:
function readfile(myfile) {
var mydata;
$.get(myfile, function (data) {
mydata = JSON.parse(data);
console.log(mydata); // result ok
});
console.log(mydata); // undefined
return (mydata); // return 'undefined'
}
var content = readfile('data.json'); //should be an array
console.log(content); // undefined
You're almost there!
The jQuery $.get() method is an asynchronous call. That means that instead of making the request to get myfile, waiting until it is complete, and then continuing from there, the code will make the request and continue on while the request is done in the background.
There are two things you can do here.
The first thing you can do is simply move your logic inside the callback function like so
$.get(myfile, function (data) {
mydata = JSON.parse(data);
console.log(mydata); // do whatever you need
});
However, if you want to continue using the readfile method, you can make the $.get request synchronous, wait for the response, then return from it.
Like so:
function readfile(myfile) {
var mydata;
$.get({
url: myfile,
async: false,
success: function(data) {
mydata = data;
}
});
return mydata;
}
Get is asynchronous meaning that it will not execute in the order it is written.
$.get(myfile, function (data) {
mydata = JSON.parse(data);
console.log(mydata); // result ok
});
This is why
console.log(mydata); // undefined
return (mydata);
is undefined, because the values are not actually set from get().
I'm facing an issue with deferred usage where 2 nested function that should wait for each other actually run in the wrong order silently.
I cant' figure out where I mix return promise.
So here is what I try to achieve. In a mobile Cordova app, when user enter the Game view, I got a function that download question in WebSql, and I want then to retrieve one question, and then my slider function load the content.
So I nested the getQuestion function in the .done() event.
router.addRoute('game', function () {
'use strict';
//Reload Question List when User enter the Game view.
questionService.initialize().done(
//Now we got question, initialize the Game View
questionService.getQuestion().done(
function (data) {
console.log(data);
slider.slidePage(new GameView(data).render().$el);
})
);
Here is how I use the $.Deferred() in both function. First I declare my $.Deferred() and at the end of the function I return the promise.
But my getQuestion() in code below does not wait for the initialize() function to end before start.
Where did I mixed up my promise return?
var getQuestions = function(param) {
var deferred = $.Deferred();
param = param;
$.ajax({
type: 'POST',
url: 'myserver',
data: {
region: uRegion
},
success: function(value, status) {
//do something with value
this.db = window.openDatabase('database details');
this.db.transaction(function(tx) {
storeQuestion(tx);
}, function(error) {
deferred.reject('Transaction error: ' + error);
}, function() {
//Transaction success
deferred.resolve();
});
},
error: function(textStatus, exception) {}
});
return deferred.promise();
};
You are not passing the success callback handler. As per current implementation your are invoking getQuestion() immediately.
Use anonymous function, rest its fine
//Reload Question List when User enter the Game view.
questionService.initialize().done(function(){
//Now we got question, initialize the Game View
questionService.getQuestion().done(
function (data) {
console.log(data);
slider.slidePage(new GameView(data).render().$el);
})
});
Libraries used : Q and underscore.
This is the following scenario in my code.
pollEngine : _.throttle(function() {
return that.fetchStatus()
.then(function(data) {
if(data) {
//keep polling until the data returned is false
return that.pollEngine();
}
else {
return;
}
});
}, 1000);
fetchStatus: function() {
var deferred = Q.defer();
$.ajax({
//code
success: function(data) {
deferred.resolve('true' or 'false' depending on data);
}
error: function() {
deferred.reject('error');
}
});
return deferred.promise;
}
This code executes first time immediately and if the data returned is true, the subsequent ajax calls are made at 1 second intervals due to throttle.
I want to delay the ajax call even first time by a few milli seconds as the server data might not be ready. But whatever code I try using delay, throttle etc. the fetchStatus function and the subsequent ajax calls are performed immediately .
I am facing the following synchronization issue. I wouldn't be surprised if it has a simple solution/workaround. The BuildMenu() function is called from another block of code and it calls the CreateMenuData() which makes a request to a service which return some data. The problem is that since it is an async call to the service when the data variable is being used it is undefined. I have provided the js log that also shows my point.
BuildMenu: function () {
console.log("before call");
var data=this.CreateMenuData();
console.log("after call");
//Doing more stuff with data that fail.
}
CreateMenuData: function () {
console.log("func starts");
data = [];
dojo.forEach(config.layerlist, function (collection, colindex) {
var layersRequest = esriRequest({
url: collection.url,
handleAs: "json",
});
layersRequest.then(
function (response) {
dojo.forEach(response.records, function (value, key) {
console.log(key);
data.push(key);
});
}, function (error) {
});
});
console.log("func ends");
return data;
}
Console log writes:
before call
func starts
func ends
after call
0
1
2
3
4
FYI: using anything "dojo." is deprecated. Make sure you are pulling all the modules you need in "require".
Ken has pointed you the right direction, go through the link and get familiarized with the asynchronous requests.
However, I'd like to point out that you are not handling only one async request, but potentionally there might be more of them of which you are trying to fill the "data" with. To make sure you handle the results only when all of the requests are finished, you should use "dojo/promise/all".
CreateMenuData: function (callback) {
console.log("func starts");
requests = [];
data = [];
var scope = this;
require(["dojo/_base/lang", "dojo/base/array", "dojo/promise/all"], function(lang, array, all){
array.forEach(config.layerlist, function (collection, colindex) {
var promise = esriRequest({
url: collection.url,
handleAs: "json",
});
requests.push(promise);
});
// Now use the dojo/promise/all object
all(requests).then(function(responses){
// Check for all the responses and add whatever you need to the data object.
...
// once it's all done, apply the callback. watch the scope!
if (typeof callback == "function")
callback.apply(scope, data);
});
});
}
so now you have that method ready, call it
BuildMenu: function () {
console.log("before call");
var dataCallback = function(data){
// do whatever you need to do with the data or call other function that handles them.
}
this.CreateMenuData(dataCallback);
}
I have a function that accepts a callback function where I pass the data back in. Can this converted to a deferred object for better practice?
Here is what I got:
var chapters;
var getChapters = function (fnLoad) {
//CACHE DATA IF APPLICABLE
if (!chapters) {
//CALL JSON DATA VIA AJAX
$.getJSON('/chapters.txt')
.done(function (json) {
//STORE DATA IN LOCAL STORAGE
chapters = Lawnchair(function () {
this.save(json, function (data) {
//CALL CALLBACK ON DATA
fnLoad(data);
});
});
});
} else {
//RETURN ALREADY CREATED LOCAL STORAGE
chapters.all(function (data) {
//CALL CALLBACK ON DATA
fnLoad(data);
});
}
};
Then I simply use it like this:
this.getChapters(function (data) {
console.log(data);
});
How can I use it like a promise though while maintaining the cache approach?
this.getChapters().done(function (data) {
console.log(data);
});
var chapters;
var getChapters = function (fnLoad) {
var d = new $.Deferred();
//CACHE DATA IF APPLICABLE
if (!chapters) {
//CALL JSON DATA VIA AJAX
$.getJSON('/chapters.txt')
.done(function (json) {
//STORE DATA IN LOCAL STORAGE
chapters = Lawnchair(function () {
this.save(json, function (data) {
//CALL CALLBACK ON DATA
d.resolve(data);
});
});
})
.fail(function() { d.reject(); });
} else {
//RETURN ALREADY CREATED LOCAL STORAGE
chapters.all(function (data) {
//CALL CALLBACK ON DATA
d.resolve(data);
});
}
return d.promise();
};
Relevant example
I see you have already accepted an answer, however if you take a large mental leap and store a promise of chapters instead of the chapters themselves, then the code will simplify significantly.
These days, this is probably the more generally adopted approach for a "fetch/cache" situation.
var chapters_promise;
var getChapters = function () {
//Cache data if applicable and return promise of data
if (!chapters_promise)
chapters_promise = $.getJSON('/chapters.txt').then(Lawnchair).then(this.save);
return chapters_promise;
};
What is actually promised (the chapters) will be determined by the value(s) returned by the functions Lawnchair and this.save, so you still have some work to do.
getChapters() will always return a promise, regardless of whether the data needs to be fetched or is already cached. Therefore, getChapters() can only be used with promise methods .then(), .done(), .fail() or .always(), for example :
getChapters().then(fnLoad);
You have no other way to access the chapters but that is reasonable since at any call of getChapters(), you don't know whether it will follow the $.getJSON() branch or the simple return branch, both of which return an identical promise.