Given a promise object is it possible to browse through the different functions attached to it?
So having this situation (using angular):
var promise = $http({ method: 'GET', url: '/random' });
promise.then(function () { console.log('Value1'); });
promise.then(function () { console.log('Value2'); });
promise.then(function () { console.log('Value4'); });
promise.then(function () { console.log('Value5'); });
Is there a way to access to:
Get access to the n function
Add a function in between the current chain
var promise = $http({ method: 'GET', url: '/random' });
promise.then(function () { console.log('Value1'); });
promise.then(function () { console.log('Value2'); });
// New insertion:
promise.then(function () { console.log('Value3'); });
promise.then(function () { console.log('Value4'); });
promise.then(function () { console.log('Value5'); });
Get access to the nth function
No. The only purpose of a promise object is to execute the then callbacks when it is resolved. It's not supposed to let you manage the callbacks.
Of course, there may always be an implementation-dependent way to do that.
Add a function in between the current chain
No. The Promises/A+ spec mandates that "all respective […] callbacks must execute in the order of their originating calls to then." Therefore, you cannot insert something dynamically.
If you need such functionality, I'd recommend to use an array of things to do that you manage yourself. Then add a single callback to your promise that carries these tasks out in the order that you want.
isn't it possible to put it in your function with an argument ?
Exemple
var promise = $http({ method: 'GET', url: '/random' })
.success(function(data, status, headers, config) {
promise.resolve('value2');
})
promise.then(function (value) {
if(value == 'value1') {
console.log('Value1');
}
if(value == 'value2') {
console.log('Value2');
}
});
Related
I've reviewed all the related topics, but for some reason am unable to understand the correct syntax of resolving my case.
It's Javascript on Framework7 platform.
There is a function that has a $.each inside, which cycles through an array of items and executes an async POST operation to update that item.
There's a requirement to update items in two separate locations, so I execute this function twice, each time with a different arguments.
What would be the best approach to chain those two functions together in a sequence to be able to reload the page, as all async requests have been executed?
Thank you in advance!
UPDATE!
I managed to get this far, but promises.then doesn't result into "Success":
var tasks = [1,2,3];
var docs = [3,4,5];
var functions = [asyncOperation(tasks),asyncOperation(docs)]
var asyncOperation = function(items) {
return new Promise(function (resolve, reject) {
var deferreds = [];
items.forEach(function(i,e){
deferreds.push(
app.request({
url: buildurlnew,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown ) {}
)
});
return deferreds;
});
}
var promises = Promise.all(functions);
promises.then(function(results) {
//console.log("Success");
});
UPDATE 2 - The changed code, as per suggestions
var processtasks = function(array, dig) {
var getlistname = GetItemTypeForListName("Alerts")
var itemProperties = {'Title' :"Test"};
var UpdatePayload = {'__metadata': {'type': getlistname}};
for(var prop in itemProperties){
UpdatePayload[prop] = itemProperties[prop]
}
var buildurl = "<REST URL to Sharepoint list>"
var buildheader = { "Accept": "application/json;odata=verbose", "X-RequestDigest" : dig, "X-HTTP-Method": "MERGE", "If-Match": "*"}
return Promise.all(array.map(function(item) {
buildurlnew = buildurl+"("+item+")";
return app.request({
url: buildurlnew,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown) {}
});
}));
}
var processitems = function(listName, array, dig, type, source, web) {
var getlistname = GetItemTypeForListName(listName)
var buildurl = "<REST URL to Sharepoint list>"
var buildheader = { "Accept": "application/json;odata=verbose", "X-RequestDigest" : dig, "If-Match": "*"}
return Promise.all(array.map(function(item) {
var itemProperties = {'UserId' : app.data.UserID, 'Title' : item};
var UpdatePayload = {'__metadata': {'type': getlistname}};
for(var prop in itemProperties){
UpdatePayload[prop] = itemProperties[prop]
}
return app.request({
url: buildurl,
type: "POST",
headers: buildheader,
contentType: "application/json;odata=verbose",
data: JSON.stringify(UpdatePayload),
success: function (data) {},
error: function(xhr, textStatus, errorThrown ) {}
});
}));
}
processitems(listName, array, dig, type, source, web).then(function(r1) {
return processtasks(ids,dig);
}).then(function(r2) {
console.log(r2);
}).catch(function(err) {
console.log(err);
});
First you have to fix your asyncOperation() function because it is not properly returning a promise that is connected to the underlying async operations you're doing.
This code doesn't really make much sense. You have a basic structure of this:
var asyncOperation = function(items) {
return new Promise(function(resolve, reject) {
// body of function here
});
}
So far so good. But, what you need to do inside that promise executor function is start some async operation and call resolve or reject when you're done. You aren't doing that. Therefore, your promise never resolves or rejects.
Instead, you are returning an array of deferreds from the promise executor callback which does nothing. The promise executor function does not expect any return value so returning a value from it does nothing. You have to indicate completion of your asynchronous operation in the promise executor by calling either resolve(...) or reject(...).
If app.request() returns a promise, then you don't even need to make your own promise at all. You can just do something like this:
var asyncOperation = function(items) {
return Promise.all(items.map(function(item) {
return app.request(...);
}));
}
asyncOperation(...).then(function(results) {
// all done here
}).catch(function(err) {
// error here
});
items.map() generates an array of promises and Promise.all() returns a new single promise that monitors that array of promise and will resolve when all the promises in the array resolve or reject when any one of them rejects.
If app.request() does not return a promise, then you probably should make a "promisified" version that does so you can use it with promise functions like Promise.all(), perhaps using util.promisify().
To run two of these in parallel (which seems practical since they don't appear to depend upon one another), you could do this:
Then, once you are properly returning a promise for your function, if you have two of these, you can just use Promise.all() on more than one function call:
Promise.all([asyncOperation(...), asyncOperation(...)]).then(function(results) {
// both done here
// not the result may be an array of arrays
}).catch(function(err) {
// error here
});
What would be the best approach to chain those two functions together in a sequence
To run them in sequence, you can do this:
asyncOperation(args1).then(function(r1) {
return asyncOperation(args2);
}).then(function(r2) {
console.log(r2);
}).catch(function(err) {
console.log(err);
});
Here is my code:
function ajaxRequest(value, path, website){
var status = false;
return new Promise(function (resolve, reject) {
window[website] = $.ajax({
url : path,
type : 'GET',
data: { "name": value,
"_token": $('meta[name="_token"]').attr('content')
},
beforeSend: function(){
if(window[website] != null) {
window[website].abort();
}
},
success: function (people) {
status = true;
resolve([status, people]);
},
error: function (jqXHR, textStatus, errorThrown) {
reject([status, textStatus]);
},
timeout: 20000
});
});
}
And I call the function like this:
ajaxRequest('Jack', 'search/twitter', 'twitter').then(function(res) { console.log(res)}, function(err){console.log(err)});
ajaxRequest('Jack', 'search/instagram', 'instagram').then(function(res) { console.log(res)}, function(err){console.log(err)});
Now I need to know those two ajax requests are done. How can I do that?
Noted that I think I have to use promise.all(), but not sure how can I use it in my case.
You are right, promise.all() is invented to solve exactly that problem.
All it does is to return a new Promise that will resolved when all the given Promises are resolved.
In your case, you can wrap your 2 ajax calls with Promise.all something like that:
promise.all([
ajaxRequest('Jack', 'search/twitter', 'twitter').then(function(res) { console.log(res)}, function(err){console.log(err)}),
ajaxRequest('Jack', 'search/instagram', 'instagram').then(function(res) { console.log(res)}, function(err){console.log(err)})
]).then(([response1, response2]) => {
// Here you execute your logic when both of the promises are resolved.
})
You can pass function calls to $.when(). Note, jQuery.ajax() returns a jQuery promise object, using Promise constructor is not necessary
$.when(ajaxRequest(), ajaxRequest())
.then(function(...results) {
// do stuff with `results` array
})
.fail(function(jqxhr, textStatus, errorThrown) {
console.error(errorThrown)
})
Here I'm using Angularjs1.x and here is my condition. If condition is success then show the table otherwise throw an error. I know some code if its Success.
AngCtrl.Js
$scope.BtnCall = function () {
ServiceCntrl.CallData().then(function (d) {
$scope.EmpData = d.data;
});
}
AngService.Js
eApp.service("ServiceCntrl", function ($http) {
var xx = '';
xx= $http({
data: formData,
method: 'post',
url: Url,
datatype: "json"
}).success(function (rsp) {
RspData = rsp;
return RspData;
}).error(function (rsp) {
console.log('Error');
});
return xx;
};
Your x.then receives two functions x.then(function(){}, function(){}); first function is called when promise is successfully resolved and second function is called if promise is rejected(failed).
If your service function is return $http promise then your first function can have a parameter named(anything you like) and it will have response data that you can use. Second function can receive error parameters if any.
you should look at angular $http service documentation.
If your service is returning the promise of the get request, then you can write
$scope.BtnCall = function () {
var x = ServiceCntrl.CallData();
x.then(function(response) {
//Success callback
//code on success goes here
//response.data
}, function(response) {
//error callback
//code on error goes here
// server returns response with an error status.
});
you can use the ng-show/ng-hide to show and hide the contents on the html page.
You can write your success/fail code as the following:
$scope.BtnCall = function() {
var x = ServiceCntrl.CallData();
x.then(function(result) {
// Success code here
// Do something and resolved the promise
}, function(reason) {
// Error code here
// You may handle/reject the reason in params
});
});
See also the Angular docs for $q.
The AngularJS $http service makes a request to the server, and returns a response
The example above executes the $http service with an object as an argument. The object is specifying the HTTP method, the url, what to do on success, and what to do on failure.
$scope.BtnCall = function () {
ServiceCntrl.CallData().then(function (d) {
$scope.EmpData = d.data;
});
}
AngService.Js :
eApp.service("ServiceCntrl", function ($http) {
var xx = '';
xx= $http({
data: formData,
method: 'post',
url: Url,
datatype: "json"
}).success(function (rsp) {
RspData = rsp;
return RspData;
}).error(function (rsp) {
console.log('Error');
});
return xx;
};
I am trying to fully understand the usage of promises and the benefits they give. I have an AJAX call that grabs a bunch of data from the server. Right now I do not have promises implemented and the code hits the server anytime the user changes a view (all using the same data, just the way it looks).
Here is the promise I am trying to add:
function feedData(arr){
//data being initialized
this.initData();
}
feedData.prototype = {
constructor: feedData,
getData:function(){
return $.ajax({
url: 'php/getData.php',
dataType: 'json',
data: {
//data being sent over
}
});
},
initData:function(){
this.getData()
.done(function(result){
console.log(result.length);
})
.fail(function(x){
console.log(x);
});
},
....
}
I may not being fully understanding asyc behavior here. What I would have liked to do is get the result from getData and populate an object full of data that would be called whenever the user changes the view. From all I've read, thats not what promises are used for. Instead I should be returning a promise and using that data again? (Maybe this is my error of thought)
So my question is, once the data from getData is returned from AJAX, is there a way to return the promise and use the .done multiple times without hitting the server ever time? Meaning, since I will be using that same data and I can't save it to a global object, how could I achieve this?
Keep track of the promise returned by $.ajax(). This makes the call only once (in the constructor) regardless of how often you call getData():
function FeedData() {
this.data_promise = $.ajax({
url: 'php/getData.php',
dataType: 'json',
data: {}
});
}
FeedData.prototype = {
constructor: FeedData,
getData: function () {
return this.data_promise;
}
}
var feed = new FeedData();
feed.getData().then(function () {
/* .. */
});
You can also delay fetching until you call getData() for the first time:
function FeedData() {
this.data_promise = null;
}
FeedData.prototype = {
constructor: FeedData,
getData: function () {
if (this.data_promise === null) {
this.data_promise = $.ajax({
url: 'php/getData.php',
dataType: 'json',
data: {}
});
}
return this.data_promise;
}
}
Note, jQuery.ajax() returns a jQuery promise object.
At first successful $.ajax() call define a property to store the data at the instance. When .then() is called assign the result of $.ajax() to the value of the property at the object as a resolved Promise.
Retrieve the value from the object using instance.property.then().
function feedData(arr) {
var feed = this;
this.getData = function() {
return $.ajax({
url: 'php/getData.php',
dataType: 'json',
data: {
//data being sent over
},
// set `context` : `this` of `$.ajax()` to current `fedData` instance
context: feed
});
};
this.initData = function() {
// note `return`
return this.getData()
.then(function(result) {
console.log(result.length);
// define `this.promise` as a `Promise` having value `result`
this.promise = Promise.resolve(result);
return result;
})
.fail(function(x) {
console.log(x);
});
}
}
var request = new feedData();
request.initData().then(function(data) {
console.log(data)
});
// this will not make antoher request
request.promise.then(function(res) {
console.log("result:", res)
});
function feedData(arr) {
var feed = this;
this.getData = function() {
// do asynchronous stuff; e.g., `$.ajax()`
return $.Deferred(function(dfd) {
dfd.resolveWith(feed, [
[1, 2, 3]
])
});
};
this.initData = function() {
// note `return`
return this.getData()
.then(function(result) {
console.log(result.length);
// define `this.promise` as a `Promise` having value `result`
this.promise = Promise.resolve(result);
return result;
})
.fail(function(x) {
console.log(x);
});
}
}
var request = new feedData();
request.initData().then(function(data) {
console.log(data)
});
// this will not make another request
request.promise.then(function(res) {
console.log("result:", res)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I have a method that is called in several places in the project. I've done method. the first method call do Ajax get, cache data in class property and fire callback. Second call method only call callback with cached data. I would like to add the ability to load data synchronously. Date should be returned by the method. I added an additional parameter to call {async: false}, but I wonder if there is a better solution using ES7 promises?
This is my callback solutions.
export class loadData {
constructor() {
this.data = [];
}
getData({callback, async = true}){
let syncData = this.data;
if( this.data.length === 0 ){
$.ajax({
beforeSend: authorizationManager.addAuthorizeHeader(),
url: apiUrl + '/Data/datadata',
dataType: 'json',
cache: true,
async: async
}).done((data)=>{
if(async) callback(data);
this.data = data;
syncData = data;
});
} else {
if(async) callback(this.data);
}
if(async === false) return syncData;
}
}
loadDataTest = new loadData();
call async
loadDataTest.getData({
callback: (data) =>{
console.log(data);
}
});
call sync
let a = loadDataTest.getData({
async: false
});
Promises are almost always the better solution. Of course they are never synchronous, but that's usually the better solution as well. This is how it would look like:
export class loadData {
constructor() {
this.promise = null;
}
getData() {
if (this.promise == null) {
this.promise = Promise.resolve($.ajax({
beforeSend: authorizationManager.addAuthorizeHeader(),
url: apiUrl + '/Data/datadata',
dataType: 'json',
cache: true
}));
}
return this.promise;
}
}
And the call:
loadDataTest.getData().then((data) => {
console.log(data);
});
I would like to add the ability to load data synchronously
I don't think you really want that. If all you want is synchronous-looking syntax for asynchronous functionality, have a look at async/await.