I got this code (using jquery)
function getData(applyFunction, callback) {
$.when(
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
).done(function(
firstData,
secondData
){
callback(applyFunction, {
firstDataToApply: { data: firstData.popup },
secondDataToApply: { data: secondData.popup }
})
})
}
Is there a way to catch separate errors from the $.getJSON part(or the when part), log those errors, and still be able to send both firstData and secondData(at the same time) to the callback function?
(I'm aware that if some or both $.getJSON fail I'd be sending empty data to the callback, and would have to null check before getting popup)
sorry for the confusing post, thanks in advance
Yes. Promises are pipelines and each handler in the pipeline can transform the value or error passing through, so you can turn a failure into a "success with null" if that's really want you want to do by adding a catch handler and returning null (or whatever value you want to transform the error into) from it. See the catch calls below and also the comments:
function getData(applyFunction, callback) {
$.when(
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
.catch(function(err) {
console.error(err);
return null;
}),
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
.catch(function(err) {
console.error(err);
return null;
})
).done(function(firstData, secondData) {
callback(applyFunction, {
firstDataToApply: {
data: firstData.popup // You'll need checks on this!
},
secondDataToApply: {
data: secondData.popup // And this!
}
});
});
}
Of course, if you're going to do this more than once or twice, you can avoid repeating yourself with a function:
function getJSONOrNull(url) {
return $.getJSON(url).catch(function(err) {
console.error(err);
return null;
});
}
then
function getData(applyFunction, callback) {
$.when(
getJSONOrNull("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
getJSONOrNull("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
).done(function(firstData, secondData) {
callback(applyFunction, {
firstDataToApply: {
data: firstData.popup // You'll need checks on this!
},
secondDataToApply: {
data: secondData.popup // And this!
}
});
});
}
Related
I'm working on a project with Vue-CLI, and here's some parts of my code;
//vuex
const member = {
namespaced:true,
state:{
data:[]
},
actions:{
getAll:function(context,apiPath){
axios.post(`http://localhost:8080/api/yoshi/backend/${apiPath}`, {
action: "fetchall",
page: "member",
})
.then(function(response){
context.commit('displayAPI', response.data);
});
},
toggle:(context,args) => {
return axios
.post(`http://localhost:8080/api/yoshi/backend/${args.address}`,
{
action:"toggle",
ToDo:args.act,
MemberID:args.id
})
.then(()=>{
alert('success');
})
},
},
mutations:{
displayAPI(state, data){
state.tableData = data;
},
},
getters:{
getTableData(state){
return state.tableData
}
}
}
//refresh function in member_management.vue
methods: {
refresh:function(){
this.$store.dispatch('member/getAll',this.displayAPI);
this.AllDatas = this.$store.getters['member/getTableData'];
}
}
//toggle function in acc_toggler.vue
ToggleAcc: function (togg) {
let sure = confirm(` ${todo} ${this.MemberName}'s account ?`);
if (sure) {
this.$store
.dispatch("member/toggle", {
address: this.displayAPI,
id: this.MemberID,
act: togg,
Member: this.MemberName,
})
.then(() => {
this.$emit("refresh");
});
}
},
The acc_toggler.vue is a component of member_management.vue, what I'm trying to do is when ToggleAcc() is triggered, it emits refresh() and it requests the updated data.
The problem is , after the whole process, the data is updated (I checked the database) but the refresh() funciton returns the data that hadn't be updated, I need to refresh the page maybe a couple of times to get the updated data(refresh() runs everytime when created in member_management.vue)
Theoretically, the ToggleAcc function updates the data, the refresh() function gets the updated data, and I tested a couple of times to make sure the order of executions of the functions are right.
However, the situation never changes. Any help is appreciated!
The code ignores promise control flow. All promises that are supposed to be awited, should be chained. When used inside functions, promises should be returned for further chaining.
It is:
refresh:function(){
return this.$store.dispatch('member/getAll',this.displayAPI)
.then(() => {
this.AllDatas = this.$store.getters['member/getTableData'];
});
}
and
getAll:function(context,apiPath){
return axios.post(...)
...
I make Ajax requests with a Promise and usually handle errors the same way. So e.g. if a 404 happens, then I would just display a standard error message by default. But in some cases I want to do something else.
Note: I'm using ExtJS 4 to do the actual Ajax request, but this issue is not specific to ExtJS. ExtJS does not use Promises, so I'm basically converting their API to a Promise API.
This is the code:
var defaultErrorHandler = function(response) {
// do some default stuff like displaying an error message
};
var ajaxRequest = function(config) {
return new Promise(function(fulfill, reject) {
var ajaxCfg = Ext.apply({}, {
success: function(response) {
var data = Ext.decode(response.responseText);
if (data.success) {
fulfill(data);
} else {
defaultErrorHandler(response);
reject(response);
}
},
failure: function(response) {
defaultErrorHandler(response);
reject(response);
}
}, config);
Ext.Ajax.request(ajaxCfg);
});
};
// usage without special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
});
// usage with special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
}, function(response) {
// do some additional error handling
});
Now the problem: The "usage without special error handling" does not work, because if I do not provide a reject function, it will throw an error. To fix this, I am forced to provide an empty function, like so:
// usage without special error handling:
ajaxRequest({url: '/some/request.json'}).then(function(data) {
// do something
}, function() {});
Having to provide an empty function every time (and in my code base this will be hundreds of times) is ugly, so I was hoping there was a more elegant solution.
I also do not want to use catch() since that would catch ALL errors thrown, even if it happens in the fulfill function. But actual errors happening in my code should not be handled, they should appear in the console.
There is no such thing a "default error handler for all promises", unless you are looking to provide an unhandled rejection handler. That would however not be restricted to the promises for your ajax requests.
The simplest and best solution would be to just expose your defaultErrorHandler and have every caller explicitly pass it the then invocation on your promise. If they don't want to use it, they either need to provide their own special error handler or they will get a rejected promise. This solution provides maximum flexibility, such as allowing to handle the rejection further down the chain.
If that is not what you want to do, but instead require immediate handling of the ajax error, your best bet is to override the then method of your returned promises:
function defaultingThen(onfulfill, onreject = defaultErrorHandler) {
return Promise.prototype.then.call(this, onfulfill, onreject);
}
function ajaxRequest(config) {
return Object.assign(new Promise(function(resolve, reject) {
Ext.Ajax.request({
...config,
success: function(response) {
var data = Ext.decode(response.responseText);
if (data.success) {
resolve(data);
} else {
reject(response);
}
},
failure: reject,
});
}), {
then: defaultingThen,
});
}
I have an alfresco webscript who return a json response.
I have a js function getWorkflowRepositoryContent() who call this webscript and get the data retuned in the response.
I store the response.json in an array list.
All works fine for me, but when i call getWorkflowRepositoryContent() from another js function, it returned an empty array when it must return an array containing the data received from webscript response.
There is the function where i return the data received from the webscript.
Can you tell me what i made a mistake, or tell me how to properly return the data from that function.
function getWorkflowRepositoryContent(){
var list=[];
var workflowFilesNameAndNodeRef;
var test=function getWorkflowFilesList(response)
{
workflowFilesNameAndNodeRef=response.json.nodes;
$.each(response.json.nodes,function(index,value){
list.push(value.name);
});
}
Alfresco.util.Ajax.request(
{
method:Alfresco.util.Ajax.GET,
url: Alfresco.constants.PROXY_URI + "/ALFRESCO-DIRECTORY",
successCallback:
{
fn:test,
scope:this
},
failureCallback:
{
fn: function(response)
{
Alfresco.util.PopupManager.displayMessage({text:"Failure"});
},
scope: this
}
});
console.log(list.length);
return list;
}
Your getWorkflowRepositoryContent is getting asynchronous data but returning synchronously so your example won't work.
An easy way would be to simple call your function with a callback argument.
function getWorkflowRepositoryContent(cb){ // pass a callback as an argument
var list=[];
var workflowFilesNameAndNodeRef;
var test=function getWorkflowFilesList(response)
{
workflowFilesNameAndNodeRef=response.json.nodes;
console.log(response.json.nodes);
$.each(response.json.nodes,function(index,value){
list.push(value.name);
});
$.each(list,function(index, fileName){
$('<option/>').val(fileName).html(fileName).appendTo('#saveButton');
$('<option/>').val(fileName).html(fileName).appendTo('#loadButton');
});
cb(list); // call the callback once the work is done
}
Alfresco.util.Ajax.request(
{
method:Alfresco.util.Ajax.GET,
url: Alfresco.constants.PROXY_URI + "/ALFRESCO-DIRECTORY",
successCallback:
{
fn:test,
scope:this
},
failureCallback:
{
fn: function(response)
{
Alfresco.util.PopupManager.displayMessage({text:"Failure To get StarXpert Workflow content"});
},
scope: this
}
});
}
getWorkflowRepositoryContent( function(list) {
console.log(list);
});
You could also use promises but it might be a little harder if you're not familiar with them.
I've just started using bluebird promises and am getting a confusing error
Code Abstract
var
jQueryPostJSON = function jQueryPostJSON(url, data) {
return Promise.resolve(
jQuery.ajax({
contentType: "application/json; charset=utf-8",
dataType: "json",
type: "POST",
url: url,
data: JSON.stringify(data)
})
).then(function(responseData) {
console.log("jQueryPostJSON response " + JSON.stringify(responseData, null, 2));
return responseData;
});
},
completeTask = function completeTask(task, variables) {
console.log("completeTask called for taskId: "+task.id);
//FIXME reform variables according to REST API docs
var variables = {
"action" : "complete",
"variables" : []
};
spin.start();
return jQueryPostJSON(hostUrl + 'service/runtime/tasks/'+task.id, variables)
.then(function() {
gwl.grrr({
msg: "Completed Task. Definition Key: " + task.taskDefinitionKey,
type: "success",
displaySec: 3
});
spin.stop();
return null;
});
}
The jQueryPostJSON function seems to work fine as is when used else where, but in that case there is data returned from the server.
When it's used within complete task, the POST is successful as can be seen on the server side, but the then function is never called instead in the console I get the error
completeTask called for taskId: 102552
bundle.js:20945 spin target: [object HTMLDivElement]
bundle.js:20968 spinner started
bundle.js:1403 Warning: a promise was created in a handler but was not returned from it
at jQueryPostJSON (http://localhost:9000/dist/bundle.js:20648:22)
at Object.completeTask (http://localhost:9000/dist/bundle.js:20743:14)
at http://localhost:9000/dist/bundle.js:21051:15
From previous event:
at HTMLDocument.<anonymous> (http://localhost:9000/dist/bundle.js:21050:10)
at HTMLDocument.handleObj.handler (http://localhost:9000/dist/bundle.js:5892:30)
at HTMLDocument.jQuery.event.dispatch (http://localhost:9000/dist/bundle.js:10341:9)
at HTMLDocument.elemData.handle (http://localhost:9000/dist/bundle.js:10027:28)
bundle.js:1403 Unhandled rejection (<{"readyState":4,"responseText":"","sta...>, no stack trace)
The warning I get the reason for, that's not the issue.
It's the Unhandled rejection and the fact that there was in fact no error from the POST.
line 21050 is here I am testing the combination of these to functions from separate modules
jQuery(document).bind('keydown', 'ctrl+]', function() {
console.log("test key pressed");
api.getCurrentProcessInstanceTask()
.then(function(task) {
api.completeTask(task);
});
});
Output from the first function call api.getCurrentProcessInstanceTask() seems to indicate it is working correctly, but here it is anyway
getCurrentProcessInstanceTask = function getCurrentProcessInstanceTask() {
if (!currentProcess || !currentProcess.id) {
return Promise.reject(new Error("no currentProcess is set, cannot get active task"));
}
var processInstanceId = currentProcess.id;
return Promise.resolve(jQuery.get(hostUrl + "service/runtime/tasks", {
processInstanceId: processInstanceId
}))
.then(function(data) {
console.log("response: " + JSON.stringify(data, null, 2));
currentProcess.tasks = data.data;
// if(data.data.length > 1){
// throw new Error("getCurrentProcessInstanceTask expects single task result. Result listed "+data.data.length+" tasks!");
// }
console.log("returning task id: "+data.data[0].id);
return data.data[0];
});
},
You're getting the warning because you are - as it says - not returning the promise from the then handler.
Where the rejection is coming from would best be tracked by catching it and logging it. That there is no stack trace suggests that you (or one of the libs you use) is throwing a plain object that is not an Error. Try finding and fixing that.
Your call should look like this:
api.getCurrentProcessInstanceTask().then(function(task) {
return api.completeTask(task);
// ^^^^^^
}).catch(function(err) {
// ^^^^^
console.error(err);
});
I'm trying to chain a promise with Angular $resource.
I have the following factory:
angular.module('myApp').factory('Product', ['$resource', function ($resource) {
return $resource(
'/api/product/:name',
{ name: '#name' },
{ 'getSub': {
url: '/api/product/getSub/:name',
method: 'GET'}
}
);
}]);
I make multiple queries using my Product factory as such:
Product.query({'name': name}, function(product) {
Product.getSub({'name': product.name}, function(subItem) {
Product.getSub({'name':subItem.name}, function(childItem) {
//do stuff with child item
})
})
})
Is there a better way to do this? I feel like nesting all these calls is not a best practice.
You can chain the promises together!
Product.query({'name': name}).$promise
.then(function(product){
return Product.getSub({'name': product.name}).$promise;
})
.then(function(subItem){
return Product.getSub({'name': subItem.name}).$promise;
})
.then(function(item){
// etc
})
you can use waterfall of async library or implement it yourself.
here's sample code for your case.
async.waterfall([
function(callback) {
Product.query({'name': name}, function(product) {
callback(null, product);
})
},
function(product, callback) {
Product.getSub({'name': product.name}, function(subItem) {
callback(null, product, subItem);
})
},
function(product, subItem, callback) {
Product.getSub({'name':subItem.name}, function(childItem) {
var result = {};
result.childItem = childItem;
result.subItem = subItem;
result.product = product;
callback(null, result);
})
}
], function (err, result) {
//do stuff with result
});
If you want the requests to be done one after another (like you have in your example) you could do a recursive function like this:
in this example i want to upload a couple of images (calling a http route):
$scope.uploadImageLayout = function (currentIndex, numberOfItems) {
if (currentIndex === numberOfItems) {
// in here you could do some last code after everything is done
} else {
Upload.upload({
url: 'localhost:3000/ficheiros',
file: $scope.imagesToUpload[$scope.auxIndex].file
}).success(function (data, status, headers, config) {
if ($scope.auxIndex < numberOfItems) {
$scope.uploadImageLayout(currentIndex + 1, numberOfItems);
}
});
}
};
and the first time you call just do this:
$scope.uploadImageLayout(0, $scope.imagesToUpload.length);
in you case its the same but instead of the Upload.upload request you should have your request and catch the callback function(s).
A useful solution maybe use $q library
https://docs.angularjs.org/api/ng/service/$q
You can use the method $q.all() to send a lot of request and manage only one callback then() or make $q.defer() and resolve por reject your oun promises.
I currently answer this question from a mobile device and i can't make an example. Sorry about that.
If when I get home that mistake trains still try to help