Make this code run more synchronously - javascript

Here is the code:
var getFile = document.getElementById("roll");
var init = getFile.files;
var rawResults = [];
if(init.length > 1){
for(var i = 0, ii = init.length; i < ii; i++){
Papa.parse(init[i], {
delimiter: "", // auto-detect
newline: "", // auto-detect
header: true,
dynamicTyping: false,
preview: 0,
encoding: "",
worker: false,
comments: false,
step: undefined,
complete: function(results, file) {
rawResults.push(results.data);
},
error: undefined,
download: false,
skipEmptyLines: false,
chunk: undefined,
fastMode: undefined,
beforeFirstChunk: undefined,
withCredentials: undefined
});
}
var flattening = _.flatten(rawResults);
console.log(rawResults);
console.log(rawResults.length);
}
When I try to run the _.flatten underscore function on the rawResults array, it comes up empty because the array isn't ready with the data due to the getFile function being asynchronous in nature.
The script takes an uploaded file, is then parsed via Papa Parse and the results then finally populated into the rawResults array.
I tried to create a callback function like this:
function firstfunction(callbackfxn){
// Put the asynchronous getFile code here.
callbackfxn();
};
function secondfunction(){
firstfunction(function(){
// Put the parsing code in here
});
};
I tried other callback variations but had no luck in getting this to work like it's supposed to.

Why not use some control flow library like bluebird or async.js?
Here's an example using async#each
async.js
async.each(init, function(file, callback) {
Papa.parse(init[i], {
delimiter: "", // auto-detect
newline: "", // auto-detect
header: true,
dynamicTyping: false,
preview: 0,
encoding: "",
worker: false,
comments: false,
step: undefined,
complete: function(results, file) {
rawResults.push(results.data);
callback();
},
error: undefined,
download: false,
skipEmptyLines: false,
chunk: undefined,
fastMode: undefined,
beforeFirstChunk: undefined,
withCredentials: undefined
});
}, function(error) {
// do something if error
var flattening = _.flatten(rawResults);
console.log(rawResults);
console.log(rawResults.length);
});
Loops through each element of the array applying the iterator function to the element. Once the complete event is triggered, it calls the callback function to tell the function that it's completed. The last argument to async#each is the error function. It's invoked once all elements have been processed. rawResults should be populated once this function is invoked.

Related

Re-use a defined variable in multiple Nightwatchjs test steps

Ok, so I know this is probably going to be something obvious and straightforward, but I just can't seem to get it to work.
Using the Nigthwatchjs perform command, I'm trying to re-use a variable from a step into the next step.
Below is a snippet of my code from my test script;
'2) Breadcrumbs': function (browser) {
browser.perform(function() {
var testMake = browser.globals.specsTestMake;
var testModel = browser.globals.specsTestModel;
var testRange = browser.globals.specsTestRange;
var testYear = browser.globals.specsTestYear;
browser.assert.containsText('nav.site-breadcrumbs', 'Home', testMake, testModel, testRange, testYear, 'specs');
console.log('test make from step 2 = ', testMake);
});
},
'3) Heading': function (browser) {
browser.perform(function(testMake, done) {
console.log('test make from step 3 = ', testMake);
done();
});
},
The console.log from the first step, '2) Breadcrumbs' is correctly displayed (for this test example) as BMW, as defined in the globals file.
I therefore thought that the console.log for the second step, '3) Heading' would also be correctly displayed as BMW.
However, this is not the case.
The second console.log displays what appears to be the entire page, settings, etc.
A small snippet is below;
✔ 2) Breadcrumbs (26ms)
test make from step 3 = { capabilities:
{ acceptInsecureCerts: true,
acceptSslCerts: true,
applicationCacheEnabled: false,
browserConnectionEnabled: false,
browserName: 'chrome',
chrome:
{ chromedriverVersion: '2.44.609545 (c2f88692e98ce7233d2df7c724465ecacfe74df5)',
userDataDir:
'/var/folders/6x/3kzp0zhd6vv0q_mfstkxd73m0000gr/T/.org.chromium.Chromium.iDQCXI' },
cssSelectorsEnabled: true,
databaseEnabled: false,
'goog:chromeOptions': { debuggerAddress: 'localhost:52102' },
handlesAlerts: true,
Any idea on what I'm doing wrong with my perform command?
Thanks

Specify response.request.responseURL in moxios

I'm currently trying to write some functionality that requires the response.request.responseURL value to be set when axios handles the response. However, I can't seem to set this value with moxios. Here's what my test looks like:
it('resets list selection if input contents were replaced', (done) => {
component.currently_selected_list_value = 10;
component.last_called_url = 'henkiehoutman';
let input = domElt.querySelector('input');
ReactTestUtils.Simulate.change(input);
moxios.wait(() => {
let request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: [{
customer_info_customers: ['', '', '', '', ''],
domain_info_customers: {},
}],
request: {responseURL: 'banaan'}
}).then(() => {
// The contents were replaced, so it's best to forget whatever our selection was.
expect(component.currently_selected_list_value).toEqual(-1);
done();
});
});
});
This is what my actual application looks like:
onChange (event) {
return axios.get(
this.props.ApiUrl + encodeURIComponent(event.target.value)
).then(
(response) => {
let response_url = response.request.responseURL; // This is the value I want to set manually.
if(this.shouldHandleResponse(response_url)){
this.last_called_url = response_url;
let data = response.data;
this.setState({results: data, dropdown: data.length > 0});
if ( this.currently_selected_list_value > max_index) {
this.currently_selected_list_value = max_index;
}
}
}
);
},
However, when I log what this value is, it just says undefined. And this is what the actual response.request value is (put it in a quote so it's a bit more readable):
LOG: Request{resolve: function (a) { ... }, reject: function (a) { ... }, config: Object{adapter: function mockAdapter(config) { ... }, transformRequest: Object{0: ...}, transformResponse: Object{0: ...}, timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, validateStatus: function validateStatus(status) { ... }, headers: Object{Accept: ..., X-CSRFToken: ...}, method: 'get', url: 'my_api_url/', data: undefined}, headers: Object{Accept: 'application/json, text/plain, /', X-CSRFToken: 'my_csrf_token'}, url: 'my_api_url/', timeout: 0, withCredentials: false, responseType: undefined}
This breaks my application, because a responseURL should always be defined. It looks like it's overwriting the request I defined in moxios. This is not necessarily a bad thing, because I assume it needs these things to function properly. However, it would be nice if I could add some values to this request.
So; How do I add a responseURL value to this request in moxios?
Turns out that it's not possible to set the responseURL because of the way I chained it to respondWith(). After checking this function in the moxios source code I saw that the values you pass in here really only have to do with actual data being returned, not an actual response object.
I also saw that the respondWith() function returns a Promise object, which is quite logical. I figured that I should not set anything on the Promise, but on the actual request. So I assigned the returned Promise to a promise variable and unchained the .then() part of the test. I set the request'sresponseURL after that. Then I called promise.then() again to assert that my test has passed.
Here is what I eventually went for:
it('resets list selection if input contents were replaced', (done) => {
component.currently_selected_list_value = 10;
component.last_called_url = 'henkiehoutman';
let input = domElt.querySelector('input');
ReactTestUtils.Simulate.change(input);
moxios.wait(() => {
let request = moxios.requests.mostRecent();
let promise = request.respondWith({
status: 200,
response: [{
customer_info_customers: ['', '', '', '', ''],
domain_info_customers: {},
}],
});
request.responseURL = 'banaan';
promise.then(() => {
// The contents were replaced, so it's best to forget whatever our selection was.
expect(component.currently_selected_list_value).toEqual(-1);
done();
});
});
});

Fine-uploader Initial File List onStatusComplete null result

I want to do the same feature as found in this SO Post ;
But in the onStatusChange callback the objects are null.
callbacks: {
onStatusChange: function(id, oldStatus, newStatus) {
console.log('new status of ' + newStatus + ' for ID: ' + id);
console.log(this.getItemByFileId(id));
}
I get the following output
new status of upload successful for ID: 0
fine-uploader.min.js:2 [Fine Uploader 5.14.2] Caught exception in 'onStatusChange' callback - Cannot read property 'className' of null
I know session response from my server is OK, b/c fine-uploader displays my file, filename and the delete button.
Is what I'm trying to do supported?
Here's my full fine-uploader code for reference:
`
var uploader_132963 = new qq.FineUploader({
element: document.getElementById("uploader_132963"),
session: { endpoint: 'https://localhost/session', params : { account: 'DEMO9', index: 1, psuuid: UUID_UPLOAD1},},
template : 'qq-template1',
debug: true,
request : {
endpoint: 'localhost',
},
autoUpload: true,
retry: {
enableAuto: true
},
multiple: false,
concurrent: {
enabled: false
},
chunking: {
concurrent: {
enabled : false,
},
enabled: true,
mandatory: true,
partSize: 2000000,
success: {
endpoint: 'https://localhost/success'
}
},
deleteFile: {
enabled: true,
endpoint: 'https://localhost',
method: 'POST',
},
extraButtons: {
folders: false
},
validation: {
allowedExtensions: ['3g2','asf','avi','bmp','doc','docx','flv','gif','jpeg','jpg','m4a','m4v','mj2','mov','mp3','mp4','pdf','png','ppt','pptx','svg',],
allowEmpty: false,
itemLimit: 1,
sizeLimit: 1024000000,
},
callbacks: {
onStatusChange: function(id, oldStatus, newStatus) {
if (newStatus == qq.status.UPLOAD_SUCCESSFUL) {
var fileItem = this.getItemByFileId(id); // will throw exception here
}
}
}
})
`
I had the exact same issue as described here. The solution was as pointed out by bobflorian. This is how I handle both canned files loaded from the server normal uploaded files:
onAllComplete: function( arrSucceeded, arrFailed,) {
if (arrSucceeded!==null && $.isArray(arrSucceeded)){
for (var i=0,x=arrSucceeded.length;i<x;i++){
//Get the template markup for the uploaded file
var fileItem = this.getItemByFileId(arrSucceeded[i]);
//Get the generated uuid. This is the same uuid that we save in the PHP SESSION. It points to the actual uploaded file
var uuid = this.getUuid(arrSucceeded[i]);
}
}
}
I'm using version 5.16.2. Ray, you did a fantastic job with this library.
Moving my code to the onAllComplete callback gives the desired result when loading files via the Initial File List. The onStatusChange doesn't seem to have the getItemByFileId function available under this at that point in time. It will throw an exception of
Caught exception in 'onStatusChange' callback - Cannot read property 'className' of null

setDeleteFileParams doesnt seem to work

I am using 5.3.2 in basic mode as I need control over the UI.
I have added code to allow the uploads and then created little UI elements that can then trigger a deletion. I need to know the filename when I am deleting. So I used setDeleteFileParams but nothing is attached to the request.
var uploader = new qq.FineUploaderBasic({
button: document.getElementById('btnUploadFiles'),
debug: true,
autoUpload: true,
request: {
paramsInBody: true,
endpoint: '../myendpoint.htm',
params: {
tempID: 'myidwhatever'
}
},
deleteFile: {
enabled: true,
forceConfirm: false,
method: 'POST',
endpoint: '../myendpoint.htm'
},
callbacks: {
onSubmitted: function(id, name){
//do work
},
onDelete: function(id) {
this.setDeleteFileParams({filename: this.getName(id)}, id);
},
onDeleteComplete: function(UID, xhr, isError){
//remove my UI element
},
onComplete: function(UID, name, responseJSON, xhr) {
//create an element and stick it in
}
}
})
//ADD THE DELETE BUTTON ACTIONS
$('uploadedFiles').addEvent("click:relay(.deleteMyFile)", function(event, element) {
event.preventDefault();
arr = element.id.split('_')
uploader.deleteFile(arr[1]);
});
Im using Mootools as my JS framework. Everything triggers ok and the console logs out the filename correctly when I delete a file but when I look at the request there is no 'filename' parameter.
Thanks for any help.
By the time your onDeleteFile callback has been called, the file is already setup to be deleted. If you'd like to influence (or prevent) the underlying request, you'll need to put your logic inside of a onSubmitDelete callback handler instead.
For example:
callbacks: {
onSubmitDelete: function(id) {
console.log(this.getName(id));
this.setDeleteFileParams({filename: this.getName(id)}, id);
}
}

Download multiple files using PapaParse?

I'm using PapaParse to download CSV files from my JavaScript scripts and it's working great.
However, I've got a page where I need to download two files and only then do some work, and I was wondering if there was a neater way to do this than this:
Papa.parse(url_seriesy, {
download: true,
header: true,
keepEmptyRows: false,
skipEmptyLines: true,
error: function(err, file, inputElem, reason) { // handle },
complete: function(y_results) {
Papa.parse(url_seriesx, {
download: true,
header: true,
keepEmptyRows: false,
skipEmptyLines: true,
error: function(err, file, inputElem, reason) { // handle },
complete: function(x_results) {
console.log(x_results.data);
}
});
}
});
This works, but is pretty unwieldy. Is there anything else I can do? Perhaps I could use promises?
If I understand correctly, you want to parse each file and then do something once all the results are collected. There are a few ways to do it but this is one way I might do it (Note: I haven't run this code; it probably needs tweaking):
var files = ["file1.csv", "file2.csv"];
var allResults = [];
for (var i = 0; i < files.length; i++)
{
Papa.parse(files[i], {
download: true,
header: true,
skipEmptyLines: true,
error: function(err, file, inputElem, reason) { /* handle*/ },
complete: function(results) {
allResults.push(results);
if (allResults.length == files.length)
{
// Do whatever you need to do
}
}
});
}

Categories