Jquery Ajax prevent fail in a deferred sequential loop - javascript

So, I'm chaining together sequential ajax, to load an array of urls in order. Originally I used .then() instead of .always(), and either way it works fine - provided that all urls existed. However, since there's a possibility of missing files, I wanted to compensate for that, and then finally, inform the user of which files were missing, in order to make it easier to rectify.
However, the problem is, on a missing file/404, the code executes, like it should, but then exits the loop, preventing any further ajax calls. So I figure, I need some way of either handling the fail() and forcing a success regardless, or some other way of overriding the default behavior on a 404, so it continues progressing through the loop.
Unfortunately, the closest Google results, were how to do the opposite (force a failure on success).
var missing=[];
uLoadList.reduce(function(prev, cur, index) {
return prev.then(function(data) {
return $.ajax("/wiki/"+cur).always(function(data) {
var temp = $('#mw-content-text',data);
temp = $('pre',temp);
if(temp.length > 0)
{
//handle success
}else{
//handle failure
missing.push(cur);
}
});
});
}, $().promise()).done(function() {
if(missing.length > 0)
{
//notify of missing objects
}
//continue on.
});
One final note, to alleviate confusion: the URLs, and the script itself, are on a MediaWiki site - so even if a 404 is returned, there will always be page content, and will contain the element with the id of "mw-content-text".

Try
(function ($) {
$.when.all = whenAll;
function whenAll(arr) {
"use strict";
var deferred = new $.Deferred(),
args = !! arr
? $.isArray(arr)
? arr
: Array.prototype.slice.call(arguments)
.map(function (p) {
return p.hasOwnProperty("promise")
? p
: new $.Deferred()
.resolve(p, null, deferred.promise())
})
: [deferred.resolve(deferred.promise())],
promises = {
"success": [],
"error": []
}, doneCallback = function (res) {
promises[this.state() === "resolved"
|| res.textStatus === "success"
? "success"
: "error"].push(res);
return (promises.success.length
+ promises.error.length) === args.length
? deferred.resolve(promises)
: res
}, failCallback = function (res) {
// do `error` notification , processing stuff
// console.log(res.textStatus);
promises[this.state() === "rejected"
|| res.textStatus === "error"
? "error"
: "success"].push(res);
return (promises.success.length
+ promises.error.length) === args.length
? deferred.resolve(promises)
: res
};
$.map(args, function (promise, index) {
return $.when(promise).always(function (data, textStatus, jqxhr) {
return (textStatus === "success")
? doneCallback.call(jqxhr, {
data: data,
textStatus: textStatus
? textStatus
: jqxhr.state() === "resolved"
? "success"
: "error",
jqxhr: jqxhr
})
: failCallback.call(data, {
data: data,
textStatus: textStatus,
jqxhr: jqxhr
})
})
});
return deferred.promise()
};
}(jQuery));
// returns `Object {
// success: Array[/* success responses*/],
// error: Array[/* error responses */]
// }`
// e.g.,
var request = function (url, data) {
return $.post(url, {
json: JSON.stringify(data)
})
}, settings = [
["/echo/json/", "success1"], // `success`
["/echo/jsons/", "error1"], // `error`
["/echo/json/", "success2"], // `success`
["/echo/jsons/", "error2"], // `error`
["/echo/json/", "success3"] // `success`
];
$.when.all(
$.map(settings, function (p) {
return request.apply($, p)
})
)
.then(function (data) {
console.log(data);
// filter , process responses
$.each(data, function(key, value) {
if (key === "success") {
results.append(
"\r\n" + key + ":\r\n" + JSON.stringify(value, null, 4)
)
} else {
results.append(
"\r\n" + key + ":\r\n"
+ JSON.stringify(
value.map(function(v, k) {
v.data.responseText = $(v.data.responseText)
.filter("title, #summary, #explanation")
.text().replace(/\s+/g, " ");
return v
})
, null, 4)
)
}
})
}, function (e) {
console.log("error", e)
});
jsfiddle http://jsfiddle.net/guest271314/620p8q8h/

Related

$http promise is not waiting to finish loop inside of it

I was trying to run AngularJS forEach loop inside a $http request where as promise is not waiting to complete the loop and before that its returning.
Please find my code below:
return $http({
method: 'GET',
url: $rootScope.baseUrl + 'test/test/test/test',
headers: {
"token": token
}
})
.then(function(responce) {
var dashboardCheck = function() {
var defer = $q.defer();
angular.forEach($rootScope.getDashboardDetails.dashboardList, function(value, key) {
if (value.name == "Dashboard" && value.contentDashboard == true) {
//return value;
defer.resolve(value);
}
})
return defer.promise;
}
var availableDashboard = function() {
return $rootScope.getDashboardDetails.dashboardList[0];
}
var defaultDash = function(value) {
method goes here
}
if (dashboardCheck()) {
defaultDash(dashboardCheck());
} else {
defaultDash(availableDashboard())
}
})
You seem to make everything way more complicated than it should be, you want to find a certain dashboard and if not find just return the first.
There is no async code after making the request and you don't do anything with the request so I'm not sure why you're making it in the first place.
The much simpler version of what you're trying to do would be this:
return $http({
method: 'GET',
url: $rootScope.baseUrl + 'test/test/test/test',
headers: {
"token": token
}
})
.then(function(responce) {
var scopeDashboard = $rootScope.getDashboardDetails.dashboardList;
var dashboard =
//not sure why need to return a defer here, no async code provided
scopeDashboard.filter(
dashboard=>
dashboard.name == "Dashboard" && dashboard.contentDashboard == true
)[0] || scopeDashboard[0];
// if scopeDashboard is an object try this
// scopeDashboard[
// Object.keys(scopeDashboard)
// .filter(
// key=>
// scopeDashboard[key].name == "Dashboard" &&
// scopeDashboard[key].contentDashboard == true
// )[0] || 0
// ];
return [dashboard,response];
})
.then(
([dashboard,response])=>{
//you now have the response and dashboard, what would you like to do with it?
}
)
.catch(
err=>console.error("something went wrong",err)
)

Attaching files stopped working octobercms email ajax form

The below setup has worked until it stopped and right now I'm perplexed why.
I've created a contact form with file attachment in OctoberCMS as below
{{ form_ajax('ContactForm::onSend', { files: 'true', flash: 'true', 'data-request-files':true, 'data-request-validate': true }) }}
<input type="hidden" name="handler" value='onSave'>
<fieldset class="form">
<input type="name" name="name" placeholder="Imię i nazwisko" required>
<input type="email" name="email" placeholder="E-mail" required>
<input type="phone" name="phone" placeholder="Telefon">
<input type="text" name="subject" placeholder="Temat" >
<textarea name="theMessage" placeholder="Zapytanie" required style="width: 100%; height: 140px;"></textarea>
<input type="file" name="fileAttachment" id="fileAttachment" class="inputfile" data-multiple-caption="wybrano {count}" /><label for="fileAttachment">wybierz plik </label><span class='attachmentName'></span>
</fieldset>
<button type="submit" class="send" data-attach-loading>Wyślij</button>
</fieldset>
{{ form_close() }}
The component for sending email
<?php namespace Depcore\Parts\Components;
use Cms\Classes\ComponentBase;
use Mail;
use Lang;
use Flash;
use Input;
use Validator;
use ValidationException;
use Redirect;
use System\Models\File;
class ContactForm extends ComponentBase
{
public function componentDetails()
{
return [
'name' => 'depcore.parts::lang.components.contactFormTitle',
'description' => 'depcore.parts::lang.components.contactFormDescription'
];
}
public function defineProperties()
{
return [
'emailTo' => [
'title' => 'depcore.parts::components.emailAddress',
'description' => 'depcore.parts::components.destinationEmailDescription',
'default' => 'zamowienia#kludi.pl',
'validationPattern' => "\A[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z",
'ValidationMessage' => ''
]
];
}
public function onSend(){
$data = post();
$vars = [
'name' => Input::get('name'),
'subject' => Input::get('subject'),
'phone' => Input::get('phone'),
'theMessage' => Input::get('theMessage'),
'fileAttachment' => Input::file('fileAttachment'),
];
$rules = [
'name' => 'required',
'email' => 'required|email'
];
$validator = Validator::make($data, $rules);
if ($validator->fails())
throw new ValidationException( $validator );
else {
Mail::send('depcore.parts::mail.message', $vars, function( $message ) use ( $vars ) {
// $message->to($this->property('emailTo'));
$message->to('adam#depcore.pl');
if ($vars['fileAttachment']) {
$file = (new File())->fromPost($vars['fileAttachment']);
$message->attach($file['path']);
}
$message->subject($vars['subject']);
Flash::success('Wiadomość została wysłana.');
});
}
}
}
From what I can tell is that the Input::file('fileAttachemnt') is always returning null so I think It could be a problem with the JavaScript framework (?).
This is a weird thing that got me by surprise when working with the project an now Im stuck.
From your code it looks like by mistake you used wrong method
$vars = [
'name' => Input::get('name'),
'subject' => Input::get('subject'),
'phone' => Input::get('phone'),
'theMessage' => Input::get('theMessage'),
'fileAttachment' => Input::get('fileAttachment'), <-- here
];
your code is using this
Input::get('fileAttachemnt');
In Reality it should be this
Input::file('fileAttachemnt');
may be you updated your code and didn't notice that ;)
UPDATE
ok I guess there is some issue with File facade code (new File())
let not use that instead we can directly use file as also you are not saving that file so,
can you replace your code and check it once
$file = (new File())->fromPost($vars['fileAttachment']);
$message->attach($file['path']);
TO
$file = $vars['fileAttachment'];
$pathToFile = $file->getPathname();
$fileName = $file->getClientOriginalName();
$mime = $file->getMimeType()
$message->attach($pathToFile, ['as' => $fileName, 'mime' => $mime]);
then check it, it should work.
MORE UPDATE
I added modified version of ajax framework (added js snippet), code is taken from October cms official git repo, and just removed some part of it so it can override existing code without conflicts.
I would suggest, take this code and create ajax-fw-override.js file then include file on your page or just duplicate layout and add it at very bottom, any how it should come after October default ajax {% framework %}, so it can override its Request.
This is not good solution but considering that you can't update your cms version we can use this. (also by making duplicate layout we make sure it won't affect anywhere else).
I tested it on your site using console and it worked. so just check it out and let me know.
+ function($) {
"use strict";
var Request = function(element, handler, options) {
var $el = this.$el = $(element);
this.options = options || {};
/*
* Validate handler name
*/
if (handler === undefined) {
throw new Error('The request handler name is not specified.')
}
if (!handler.match(/^(?:\w+\:{2})?on*/)) {
throw new Error('Invalid handler name. The correct handler name format is: "onEvent".')
}
/*
* Prepare the options and execute the request
*/
var $form = options.form ? $(options.form) : $el.closest('form'),
$triggerEl = !!$form.length ? $form : $el,
context = {
handler: handler,
options: options
}
$el.trigger('ajaxSetup', [context])
var _event = jQuery.Event('oc.beforeRequest')
$triggerEl.trigger(_event, context)
if (_event.isDefaultPrevented()) return
var loading = options.loading !== undefined ? options.loading : null,
isRedirect = options.redirect !== undefined && options.redirect.length,
useFlash = options.flash !== undefined,
useFiles = options.files !== undefined
if (useFiles && typeof FormData === 'undefined') {
console.warn('This browser does not support file uploads via FormData')
useFiles = false
}
if ($.type(loading) == 'string') {
loading = $(loading)
}
/*
* Request headers
*/
var requestHeaders = {
'X-OCTOBER-REQUEST-HANDLER': handler,
'X-OCTOBER-REQUEST-PARTIALS': this.extractPartials(options.update)
}
if (useFlash) {
requestHeaders['X-OCTOBER-REQUEST-FLASH'] = 1
}
/*
* Request data
*/
var requestData,
inputName,
data = {}
$.each($el.parents('[data-request-data]').toArray().reverse(), function extendRequest() {
$.extend(data, paramToObj('data-request-data', $(this).data('request-data')))
})
if ($el.is(':input') && !$form.length) {
inputName = $el.attr('name')
if (inputName !== undefined && options.data[inputName] === undefined) {
options.data[inputName] = $el.val()
}
}
if (options.data !== undefined && !$.isEmptyObject(options.data)) {
$.extend(data, options.data)
}
if (useFiles) {
requestData = new FormData($form.length ? $form.get(0) : null)
if ($el.is(':file') && inputName) {
$.each($el.prop('files'), function() {
requestData.append(inputName, this)
})
delete data[inputName]
}
$.each(data, function(key) {
requestData.append(key, this)
})
} else {
requestData = [$form.serialize(), $.param(data)].filter(Boolean).join('&')
}
/*
* Request options
*/
var requestOptions = {
url: window.location.href,
crossDomain: false,
context: context,
headers: requestHeaders,
success: function(data, textStatus, jqXHR) {
/*
* Halt here if beforeUpdate() or data-request-before-update returns false
*/
if (this.options.beforeUpdate.apply(this, [data, textStatus, jqXHR]) === false) return
if (options.evalBeforeUpdate && eval('(function($el, context, data, textStatus, jqXHR) {' + options.evalBeforeUpdate + '}.call($el.get(0), $el, context, data, textStatus, jqXHR))') === false) return
/*
* Trigger 'ajaxBeforeUpdate' on the form, halt if event.preventDefault() is called
*/
var _event = jQuery.Event('ajaxBeforeUpdate')
$triggerEl.trigger(_event, [context, data, textStatus, jqXHR])
if (_event.isDefaultPrevented()) return
if (useFlash && data['X_OCTOBER_FLASH_MESSAGES']) {
$.each(data['X_OCTOBER_FLASH_MESSAGES'], function(type, message) {
requestOptions.handleFlashMessage(message, type)
})
}
/*
* Proceed with the update process
*/
var updatePromise = requestOptions.handleUpdateResponse(data, textStatus, jqXHR)
updatePromise.done(function() {
$triggerEl.trigger('ajaxSuccess', [context, data, textStatus, jqXHR])
options.evalSuccess && eval('(function($el, context, data, textStatus, jqXHR) {' + options.evalSuccess + '}.call($el.get(0), $el, context, data, textStatus, jqXHR))')
})
return updatePromise
},
error: function(jqXHR, textStatus, errorThrown) {
var errorMsg,
updatePromise = $.Deferred()
if ((window.ocUnloading !== undefined && window.ocUnloading) || errorThrown == 'abort')
return
/*
* Disable redirects
*/
isRedirect = false
options.redirect = null
/*
* Error 406 is a "smart error" that returns response object that is
* processed in the same fashion as a successful response.
*/
if (jqXHR.status == 406 && jqXHR.responseJSON) {
errorMsg = jqXHR.responseJSON['X_OCTOBER_ERROR_MESSAGE']
updatePromise = requestOptions.handleUpdateResponse(jqXHR.responseJSON, textStatus, jqXHR)
}
/*
* Standard error with standard response text
*/
else {
errorMsg = jqXHR.responseText ? jqXHR.responseText : jqXHR.statusText
updatePromise.resolve()
}
updatePromise.done(function() {
$el.data('error-message', errorMsg)
/*
* Trigger 'ajaxError' on the form, halt if event.preventDefault() is called
*/
var _event = jQuery.Event('ajaxError')
$triggerEl.trigger(_event, [context, errorMsg, textStatus, jqXHR])
if (_event.isDefaultPrevented()) return
/*
* Halt here if the data-request-error attribute returns false
*/
if (options.evalError && eval('(function($el, context, errorMsg, textStatus, jqXHR) {' + options.evalError + '}.call($el.get(0), $el, context, errorMsg, textStatus, jqXHR))') === false)
return
requestOptions.handleErrorMessage(errorMsg)
})
return updatePromise
},
complete: function(data, textStatus, jqXHR) {
$triggerEl.trigger('ajaxComplete', [context, data, textStatus, jqXHR])
options.evalComplete && eval('(function($el, context, data, textStatus, jqXHR) {' + options.evalComplete + '}.call($el.get(0), $el, context, data, textStatus, jqXHR))')
},
/*
* Custom function, requests confirmation from the user
*/
handleConfirmMessage: function(message) {
var _event = jQuery.Event('ajaxConfirmMessage')
_event.promise = $.Deferred()
if ($(window).triggerHandler(_event, [message]) !== undefined) {
_event.promise.done(function() {
options.confirm = null
new Request(element, handler, options)
})
return false
}
if (_event.isDefaultPrevented()) return
if (message) return confirm(message)
},
/*
* Custom function, display an error message to the user
*/
handleErrorMessage: function(message) {
var _event = jQuery.Event('ajaxErrorMessage')
$(window).trigger(_event, [message])
if (_event.isDefaultPrevented()) return
if (message) alert(message)
},
/*
* Custom function, focus fields with errors
*/
handleValidationMessage: function(message, fields) {
$triggerEl.trigger('ajaxValidation', [context, message, fields])
var isFirstInvalidField = true
$.each(fields, function focusErrorField(fieldName, fieldMessages) {
fieldName = fieldName.replace(/\.(\w+)/g, '[$1]')
var fieldElement = $form.find('[name="' + fieldName + '"], [name="' + fieldName + '[]"], [name$="[' + fieldName + ']"], [name$="[' + fieldName + '][]"]').filter(':enabled').first()
if (fieldElement.length > 0) {
var _event = jQuery.Event('ajaxInvalidField')
$(window).trigger(_event, [fieldElement.get(0), fieldName, fieldMessages, isFirstInvalidField])
if (isFirstInvalidField) {
if (!_event.isDefaultPrevented()) fieldElement.focus()
isFirstInvalidField = false
}
}
})
},
/*
* Custom function, display a flash message to the user
*/
handleFlashMessage: function(message, type) {},
/*
* Custom function, redirect the browser to another location
*/
handleRedirectResponse: function(url) {
window.location.href = url
},
/*
* Custom function, handle any application specific response values
* Using a promisary object here in case injected assets need time to load
*/
handleUpdateResponse: function(data, textStatus, jqXHR) {
/*
* Update partials and finish request
*/
var updatePromise = $.Deferred().done(function() {
for (var partial in data) {
/*
* If a partial has been supplied on the client side that matches the server supplied key, look up
* it's selector and use that. If not, we assume it is an explicit selector reference.
*/
var selector = (options.update[partial]) ? options.update[partial] : partial
if ($.type(selector) == 'string' && selector.charAt(0) == '#') {
$(selector.substring(1)).append(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR])
} else if ($.type(selector) == 'string' && selector.charAt(0) == '^') {
$(selector.substring(1)).prepend(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR])
} else {
$(selector).trigger('ajaxBeforeReplace')
$(selector).html(data[partial]).trigger('ajaxUpdate', [context, data, textStatus, jqXHR])
}
}
/*
* Wait for .html() method to finish rendering from partial updates
*/
setTimeout(function() {
$(window)
.trigger('ajaxUpdateComplete', [context, data, textStatus, jqXHR])
.trigger('resize')
}, 0)
})
/*
* Handle redirect
*/
if (data['X_OCTOBER_REDIRECT']) {
options.redirect = data['X_OCTOBER_REDIRECT']
isRedirect = true
}
if (isRedirect) {
requestOptions.handleRedirectResponse(options.redirect)
}
/*
* Handle validation
*/
if (data['X_OCTOBER_ERROR_FIELDS']) {
requestOptions.handleValidationMessage(data['X_OCTOBER_ERROR_MESSAGE'], data['X_OCTOBER_ERROR_FIELDS'])
}
/*
* Handle asset injection
*/
if (data['X_OCTOBER_ASSETS']) {
assetManager.load(data['X_OCTOBER_ASSETS'], $.proxy(updatePromise.resolve, updatePromise))
} else {
updatePromise.resolve()
}
return updatePromise
}
}
if (useFiles) {
requestOptions.processData = requestOptions.contentType = false
}
/*
* Allow default business logic to be called from user functions
*/
context.success = requestOptions.success
context.error = requestOptions.error
context.complete = requestOptions.complete
requestOptions = $.extend(requestOptions, options)
requestOptions.data = requestData
/*
* Initiate request
*/
if (options.confirm && !requestOptions.handleConfirmMessage(options.confirm)) {
return
}
if (loading) loading.show()
$(window).trigger('ajaxBeforeSend', [context])
$el.trigger('ajaxPromise', [context])
return $.ajax(requestOptions)
.fail(function(jqXHR, textStatus, errorThrown) {
if (!isRedirect) {
$el.trigger('ajaxFail', [context, textStatus, jqXHR])
}
if (loading) loading.hide()
})
.done(function(data, textStatus, jqXHR) {
if (!isRedirect) {
$el.trigger('ajaxDone', [context, data, textStatus, jqXHR])
}
if (loading) loading.hide()
})
.always(function(dataOrXhr, textStatus, xhrOrError) {
$el.trigger('ajaxAlways', [context, dataOrXhr, textStatus, xhrOrError])
})
}
Request.DEFAULTS = {
update: {},
type: 'POST',
beforeUpdate: function(data, textStatus, jqXHR) {},
evalBeforeUpdate: null,
evalSuccess: null,
evalError: null,
evalComplete: null
}
/*
* Internal function, build a string of partials and their update elements.
*/
Request.prototype.extractPartials = function(update) {
var result = []
for (var partial in update)
result.push(partial)
return result.join('&')
}
// REQUEST PLUGIN DEFINITION
// ============================
var old = $.fn.request
$.fn.request = function(handler, option) {
var args = arguments
var $this = $(this).first()
var data = {
evalBeforeUpdate: $this.data('request-before-update'),
evalSuccess: $this.data('request-success'),
evalError: $this.data('request-error'),
evalComplete: $this.data('request-complete'),
confirm: $this.data('request-confirm'),
redirect: $this.data('request-redirect'),
loading: $this.data('request-loading'),
flash: $this.data('request-flash'),
files: $this.data('request-files'),
form: $this.data('request-form'),
update: paramToObj('data-request-update', $this.data('request-update')),
data: paramToObj('data-request-data', $this.data('request-data'))
}
if (!handler) handler = $this.data('request')
var options = $.extend(true, {}, Request.DEFAULTS, data, typeof option == 'object' && option)
return new Request($this, handler, options)
}
$.fn.request.Constructor = Request
$.request = function(handler, option) {
return $(document).request(handler, option)
}
// REQUEST NO CONFLICT
// =================
$.fn.request.noConflict = function() {
$.fn.request = old
return this
}
// REQUEST DATA-API
// ==============
function paramToObj(name, value) {
if (value === undefined) value = ''
if (typeof value == 'object') return value
try {
return JSON.parse(JSON.stringify(eval("({" + value + "})")))
} catch (e) {
throw new Error('Error parsing the ' + name + ' attribute value. ' + e)
}
}
}(window.jQuery);

Cannot get promise/ajax data to return to parent function

I searched for a long time and although there are many questions with similar keywords, none of them share the same intricacies, so let me explain my problem:
I have a function (getProjectData), that has to be called (it is a system wide thing) to get all data from a particular admin section of a cms. My problem comes from a conditional check, where if A one object is blank, where if B, it needs to contain the result of more than 1 ajax calls. The whole data object gets retuned in this function. Code so far:
function getProjectData() {
var code = "some id";
var frozen = true;
var obj = {
code: code,
frozen: true,
localData: "",
};
// here is the point where the object gets returned with a black localData
// or splits to get a few ajax calls
if ( !obj.frozen ) return obj;
else {
getLocalData ( code ).then( function ( data ) {
// ideally, here i would make the parent return obj, but this time with added localData data
obj.localData = data;
parent.return obj;
} );
}
}
function getLocalData ( code ) {
var promise = new Promise ( function ( resolve, reject ) {
var apiReq = "apiUrl";
var chartsReq = "anotherApiUrl";
var res;
$.when(
// api response
$.ajax({
url: apiReq + code,
type: "get",
success: function ( data ) {
return data;
},
error: function ( error ) {
return error;
}
}),
// another ajax call..
$.ajax({ ... }),
// another ajax call..
$.ajax({ ... })
).then(
function ( a, b, c ) {
data = {
response: a
...... for b and c
};
resolve ( data );
},
function ( error ) {
reject ( error );
}
);
} );
return promise.then(
function (data) {
return data;
},
function ( error ) {
console.log ( error );
}
);
}
My problem is that since (and i tried with other ways as well..) all the returns are in a function within other functions, there is no way to return the Obj object after the localData has been added.
I might have over-engineered it but i tried many different ways and since the getProjectData is called by a save button and values need to be returned rather than say, appended to the DOM, i am not sure what to do!
I know that in asynchronous requests, the function has to return before the result is available, where we would usually use callbacks, but a callback will not be able to return something. I could use synchronous requests but i know they are going to be deprecated..
Any ideas?
The correct way to call functions is listed below.
Sincerely I don't understand what you mean saying:
// ideally, here i would make the parent return obj, but this time with added localData data
Using the following way in the parent you should just call:
getProjectData.then(function(obj){ 
//... do whatever
});
JS:
function getProjectData() {
var code = "some id";
var frozen = true;
var obj = {
code: code,
frozen: true,
localData: "",
};
// here is the point where the object gets returned with a black localData
// or splits to get a few ajax calls
return new Promise(function(resolve, reject) {
if ( !obj.frozen )
resolve(obj);
else {
getLocalData ( code ).then( function ( data ) {
// ideally, here i would make the parent return obj, but this time with added localData data
obj.localData = data;
resolve(obj);
});
});
}
}
function getLocalData ( code ) {
var promise = new Promise ( function ( resolve, reject ) {
var apiReq = "apiUrl";
var chartsReq = "anotherApiUrl";
var res;
$.when(
// api response
$.ajax({
url: apiReq + code,
type: "get",
success: function ( data ) {
return data;
},
error: function ( error ) {
return error;
}
}),
// another ajax call..
$.ajax({ ... }),
// another ajax call..
$.ajax({ ... })
).then(
function ( a, b, c ) {
data = {
response: a
...... for b and c
};
resolve ( data );
},
function ( error ) {
reject ( error );
}
);
} );
return promise;
}

Parse cloud code loop is only going through first instance

I want the following loop to go through every instance of matchCenterItem, yet for some reason, it pings ebay using the properties of only the first instance. The console logs at the end of the function however, loop through all instances and log their respective properties.
Parse.Cloud.define("MatchCenterTest", function(request, response) {
var matchCenterItem = Parse.Object.extend("matchCenterItem");
var query = new Parse.Query(matchCenterItem);
var promises = [];
query.limit(10);
query.find().then(function(results) {
for (i=0; i<results.length; i++) {
url = 'http://svcs.ebay.com/services/search/FindingService/v1';
promises.push(Parse.Cloud.httpRequest({
url: url,
params: {
'OPERATION-NAME' : 'findItemsByKeywords',
'SERVICE-VERSION' : '1.12.0',
'SECURITY-APPNAME' : '*App ID goes here*',
'GLOBAL-ID' : 'EBAY-US',
'RESPONSE-DATA-FORMAT' : 'JSON',
'REST-PAYLOAD&sortOrder' : 'BestMatch',
'paginationInput.entriesPerPage' : '3',
'outputSelector=AspectHistogram&itemFilter(0).name=Condition&itemFilter(0).value(0)' : results[i].get('itemCondition'),
'itemFilter(1).name=MaxPrice&itemFilter(1).value' : results[i].get('maxPrice'),
'itemFilter(1).paramName=Currency&itemFilter(1).paramValue' : 'USD',
'itemFilter(2).name=MinPrice&itemFilter(2).value' : results[i].get('minPrice'),
'itemFilter(2).paramName=Currency&itemFilter(2).paramValue' : 'USD',
//'itemFilter(3).name=LocatedIn&itemFilter(3).Value' : request.params.itemLocation,
'itemFilter(3).name=ListingType&itemFilter(3).value' : 'FixedPrice',
'keywords' : results[i].get('searchTerm'),
},
// success: function (httpResponse) {
// // parses results
// var httpresponse = JSON.parse(httpResponse.text);
// response.success(httpresponse);
// console.log('MatchCenter Pinged eBay dude!');
// },
// error: function (httpResponse) {
// console.log('error!!!');
// response.error('Request failed with response code ' + httpResponse.status);
// }
}));
console.log(results[i].get('itemCondition'));
console.log(results[i].get('maxPrice'));
console.log(results[i].get('minPrice'));
console.log(results[i].get('searchTerm'));
}
});
Parse.Promise.when(promises).then(function(results) {
var httpresponse = JSON.parse(httpResponse.text);
response.success(httpresponse);
}, function(err) {
console.log('error!!!');
});
});
This is because the http request is asynchronous, and you're calling response.success in the completion handler for the first (and all) requests. Use the promise syntax and only complete when they are done. Simplified concept:
var promises = [];
for (...) {
promises.push(Parse.Cloud.httpRequest({...})); // no success/error params
}
Parse.Promise.when(promises).then(function(results) {
response.success(...);
}, function(err) {
});

Jquery Deferred. Multiple ajax calls. deferred vs async false

I've got the following.
var lookupInit = function () {
http.get('api/employmenttype', null, false)
.done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
});
http.get('api/actionlist', null, false)
.done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
});
http.get('api/company', null, false)
.done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
});
//... x 5 more
return true;
};
// somewhere else
if (lookupInit(id)) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
}
else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
} else {
console.log('User info init failed!');
}
The following "http.get('api/employmenttype', null, false)" means i set async to false.
I'm aware that this is probably inefficient. And i'd like to have all the calls load simultaneously.
The only problem is if i don't have them set to async false, the second part of my code might execute before the dropdowns are populated.
I've tried a couple of attempts with Jquery Deferreds, but they have resulted in what i can only describe as an abortion.
The only thing i'm looking to achieve is that the lookup calls finish before the adimport/second part of my code, in any order.... But having each call wait for the one before it to finish EG: async, seems like the only solution I'm capable of implementing decently ATM.
Would this be an appropriate place for deferred function, and could anyone point me into a direction where i could figure out how to implement it correctly, as I've never done this before?
You can use $.when to combine multiple promises to one that resolves when all of them have been fulfilled. If I got you correctly, you want
function lookupInit() {
return $.when(
http.get('api/employmenttype').done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
}),
http.get('api/actionlist').done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
}),
http.get('api/company').done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
}),
// … some more
);
}
Then somewhere else
lookupInit(id).then(function(/* all responses if you needed them */) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
return http.json('api/adimport', {by:"latest"})
} else {
console.log('api/adimport: transaction');
return http.json('api/adimport', {by:"transaction", TransactionId:id});
}
}, function(err) {
console.log('User info init failed!');
}).done(viewInit);
In the Jquery API I've found this about resolving multiple deferreds:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively.
each argument is an array with the following
structure: [ data, statusText, jqXHR ] */
var data = a1[0] + a2[0]; /* a1[0] = "Whip", a2[0] = " It" */
if ( /Whip It/.test(data) ) {
alert("We got what we came for!");
}
});
Using this with your code:
var defer = $.when(
$.get('api/employmenttype'),
$.get('api/actionlist'),
$.get('api/company'),
// ... 5 more
);
defer.done(function (arg1, arg2, arg3 /*, ... 5 more*/) {
vm.lookups.allEmploymentTypes(arg1[0]);
vm.lookups.allEmploymentTypes(arg2[0]);
vm.lookups.allEmploymentTypes(arg3[0]);
// .. 5 more
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
} else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
});
You can use the defer of the $.when() inside an other $.when(), so if the json calls are not dependant on the first calls you can add them in a an onther defer.

Categories