First click doesn't log all info but second click does - javascript

Button wont log everything on first click but does on second click.
Pretty much it skips the function that iterate through the object.
Well I've tried putting promises and async await on most function thinking this was the problem, but to no avail.
// button code
const btn = document.querySelector("button");
btn.disabled = false;
btn.onclick = function(e) {
takeASnap()
.then(toDataURL)
.then(async function() {
Object.keys(await returnData).forEach(function(item) {
console.log(item); // key
console.log(typeof item);
console.log(item);
console.log(returnData[item]); // value
});
console.log(await returnData);
});
};
});
HTML
<div class="window">
<video></video>
<button class="snapshot">take a snapshot</button>
</div>
toDataUrl
async function toDataURL(blob) {
let reader = new FileReader();
let b64;
reader.readAsDataURL(blob);
reader.onloadend = function() {
let base64data = reader.result;
let count = 0;
let data;
// ChunkSubstr takes thousand character and put into array that is
// returned
b64 = chunkSubstr(base64data, 1000);
console.log("Hllo");
webSocket(b64);
};
}
webSocket function
This is the function that assign the returndata it's value which comes from a server.
async function webSocket(b64) {
const ws = new WebSocket("ws://192.168.1.70:3000");
ws.onopen = await function() {
console.log("Connected");
b64.forEach(element => {
ws.send(m_imageNr + " " + element);
// console.log(element);
});
m_imageNr++;
ws.onmessage = function(event) {
console.log(typeof returnData);
returnData.push(event.data);
};
};
return await returnData;
}
Expected result is that it should iterate through the object on first click but it does only do that on the second click.
EDITTED added some code that was asked for.

refactoring may help:
const btn = document.querySelector("button");
btn.disabled = false;
async function getData() {
Object.keys(await returnData).forEach(function(item) {
console.log(item); // key
console.log(typeof item);
console.log(item);
console.log(returnData[item]); // value
});
console.log(await returnData);
};
btn.onclick = function(e) {
takeASnap()
.then(toDataURL)
.then(() => await getData());
}

Related

Generating a number from an API call, then using the returned number as a parameter in another API call

I am trying to create a "Show - Actor - Show" button, which displays a random show, then a cast member from that show, then another show with the same cast member.
I have succeeded in generating a random show ID, but I can't figure out how to pass that ID into the randomActor function since randomShow is not a function.
Ideally I want to use one button the searches for an actor or show depending on the previous search. Currently the issue is with my randomActor function. I am struggling to pass in the value of the randomShow search to the string template literal.
Any help/advice is much appreciated!
const randomButton = document.querySelector('#randomShow')
const randomShow = randomButton.addEventListener('click', async function (e) {
e.preventDefault();
const randomNumber = Math.floor(Math.random() * 1000);
const res = await axios.get(`http://api.tvmaze.com/shows/${randomNumber}`)
console.log(res.data.id);
return (res.data.id)
})
const randomActor = randomActorButton.addEventListener('click', async function (e) {
e.preventDefault();
const res = await axios.get(`http://api.tvmaze.com/shows/${randomShow.res.data.id}/cast`)
console.log(res.data[0]);
return (res.data[0])
})
The eventListener will not return the result of the function in the listener
This works:
const randomButton = document.querySelector('#randomShow');
let randomShow = "";
randomButton.addEventListener('click', async function(e) {
e.preventDefault();
document.getElementById("actor").innerHTML = "";
const randomNumber = Math.floor(Math.random() * 1000);
const res = await axios.get(`https://api.tvmaze.com/shows/${randomNumber}`)
document.getElementById("show").innerHTML = res.data.name;
randomShow = res;
})
const randomActorButton = document.querySelector('#randomActor')
randomActorButton.addEventListener('click', async function(e) {
e.preventDefault();
if (randomShow) {
const res = await axios.get(`https://api.tvmaze.com/shows/${randomShow.data.id}/cast`)
console.log(res.data)
document.getElementById("actor").innerHTML = res.data[Math.floor(Math.random(res.data.length))].person.name;
}
else console.log("No show")
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js" ></script>
<button type="button" id="randomShow">Random show</button>
<button type="button" id="randomActor">Random Actor</button>
<span id="show"></span> <span id="actor"></span>

how to unsubscribe of function in method?

Working on dialog component with angular js and now I find out that my function is subscribed and in if condition do not quit method, but continuously executing another function afterClosed() , here is example of code :
openCreateNewContentDialog(): void {
const oldData = this.dataSource.data;
const dialogConfig = AppConstants.matDialogConfig();
const dialog = this.dialog.open(LicenseDialogComponent, dialogConfig);
dialog.beforeClosed().subscribe(licenceDate => {
for (const datesToCheck of oldData) {
const newDateFrom = new Date(licenceDate.expirationDateFrom);
const oldDateTo = new Date(datesToCheck.expirationDateTo.toString());
if (newDateFrom <= oldDateTo) {
// console.log('return?');
return;
}
}
});
dialog.afterClosed().subscribe(licence => {
if (licence) {
this._value.push(licence);
this.dataSource.data = this.value;
this.change();
}
});
}
What is the best and optimized way to unsubscribe beforeClosed() function?
So from your description, I understand that you dont want a second subscription to happen if the condition in the first subscriber is true, right? But you subscription will happen anyway because you instantiated it in the method, the code in the subscribe() it's just a callback. So if you dont want a lot of rewriting I will suggest storing
subscriptions in variables, so you will have an access to them and can unsubscribe at any time.
openCreateNewContentDialog(): void {
const oldData = this.dataSource.data;
const dialogConfig = AppConstants.matDialogConfig();
const dialog = this.dialog.open(LicenseDialogComponent, dialogConfig);
const beforeClosed = dialog.beforeClosed().subscribe(licenceDate => {
for (const datesToCheck of oldData) {
const newDateFrom = new Date(licenceDate.expirationDateFrom);
const oldDateTo = new Date(datesToCheck.expirationDateTo.toString());
if (newDateFrom <= oldDateTo) {
// console.log('return?');
afterClosed.unsubscribe();
return;
}
}
});
const afterClosed = dialog.afterClosed().subscribe(licence => {
if (licence) {
this._value.push(licence);
this.dataSource.data = this.value;
this.change();
}
});
}
I hope it helps! Also you can try https://www.digitalocean.com/community/tutorials/angular-takeuntil-rxjs-unsubscribe if you have to handle multiple subscriptions.

Upload multiple images in a angular and firebase project, It is not working in sequence

async onSubmit(formValue) {
this.isSubmitted = true;
if(this.selectedImageArray.length > 0) { // 4 images in this array
for (let index = 0; index < this.selectedImageArray.length; index++) { // Loop through this image array
await new Promise(resolve => {
setTimeout(()=> {
console.log('This is iteration ' + index);
var filePath = `images/tours/${this.selectedImageArray[index].name.split('.').slice(0,-1).join('.')}_${new Date(). getTime()}`;
const fileRef = this.storage.ref(filePath);
this.storage.upload(filePath, this.selectedImageArray[index]).snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe((url) => {
formValue[`imageUrl${index+1}`] = url;
console.log(url);
});
})
).subscribe()
resolve();
}, 3000);
});
}
console.log('After loop execution');
// this.value(formValue);
}
}
After submitting the code it will download and print 3 urls and then it print 'after loop execution' then it print 4th one I don't understand why. See here in console
see in the image line no of code execution.
What I want to execute code in sequence after all images download then after it will go out of loop.
I wrote another version of this that hopefully works as you expect it to.
First we create an array of all the storage upload snapshot observables.
The we use concat() to run them all in sequence. (If you change from concat() to merge() they will all go at once)
The we use mergeMap to jump over to the getDownloadURL
Then in the subscribe we add the url to the formValues
Finally in the finalize we set the class propery "value" equal to the formValue.
onSubmit(formValue) {
const snapshotObservables = this.selectedImageArray.map(selectedImage => { // 4 images in this array
const filePath = `images/tours/${selectedImage.name.split('.').slice(0, -1).join('.')}_${new Date(). getTime()}`;
return combineLatest(this.storage.upload(filePath, selectedImage).snapshotChanges(), filePath);
});
concat(...snapshotObservables).pipe(
mergeMap(([snapshot, filePath]) => {
const fileRef = this.storage.ref(filePath);
return fileRef.getDownloadURL();
}),
finalize(() => {
this.value(formValue);
})
).subscribe(url => {
formValue[`imageUrl${index+1}`] = url;
});
}
I wrote a new function for multiple file upload
public multipleFileUpload(event, isEncodeNeeded?: Boolean):Array<any> {
if(!isEncodeNeeded){
isEncodeNeeded=false;
}
let fileList = [];
for (let index = 0; index < event.target.files.length; index++) {
let returnData = {};
let file: File = event.target.files[index];
let myReader: FileReader = new FileReader();
returnData['documentName'] = event.target.files[index]['name'];
returnData['documentType'] = event.target.files[index]['type'];
myReader.addEventListener("load", function (e) {
if (myReader.readyState == 2) {
returnData['document'] = isEncodeNeeded ? btoa(e.target['result']) : e.target['result'];
}
});
myReader.readAsBinaryString(file);
fileList.push(returnData);
}
return fileList;
}
In this function event is the event of the input and the isEncodeNeeded is conversion is needed. If this is true then it convert to base64 format.
The output format is
[{
"document": documentbyte,
"documentName": document name,
"documentType": file format
}]

Manipulating data obtained with indexedDB

Im new with IndexedDB and I can not manipulate the data obtained from indexedDB table.I only need do a search values when a button is pressed, then the event activated with the button starts to work and it has to return many results, which may take a few seconds to return values, so I need to use async / await in the callback function. I think the problem is synchronous because I make the callback function async and the function getData() with which I get the data has the word await with it, even so, I can not work with the data, because when I do the console.log(x) it returns the undefined value.
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click', async function () {
let x = await getData();
console.log(x)
})
};
function getData() {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function () {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
return myRecord = objectStoreRequest.result;
};
Of course, the console.log(x) is only to check that the data obtained is correct, once that point would come the part of the search but that is another story.
I'm not sure if my problem is with async / await or because I do not get the IndexedDB data correctly. Any help?
EDIT: -- I think I have found a solution, even though I think it is not the best way to solve the problem. I have moved all the code of the function getData() within the function that invokes the event, once the data is obtained I work within the method .onsuccess of objectStoreRequest, thus I avoid having to use async / await, I also continue working on the transaction which has not yet been finalized. If someone knows a cleaner way to make it work or explain to me why the original post code does not work, I would be very grateful.
I attach the code with which I am currently working:
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click',function () {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function () {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
myRecord = objectStoreRequest.result;
console.log(myRecord)
}
})
};
Anyway it seems like no one have another way for resolve this, so Im go to respond myself this post with this response.
This is the only way I have found to solve the problem, although it seems like a dirty code, I think it could be improved.
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click',function () {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function () {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
myRecord = objectStoreRequest.result;
console.log(myRecord)
}
})
};

The changes I'm making to my array inside linereader.on are not available outside

I'm parsing a data file (which contains json data) line-by-line and creating objects. I then add these objects to an array which I have declared outside. But for some reason, my 'services' array becomes empty again outside the linereader.on function. I'm able to console.log(services) inside the linereader.on and see it printing data as expected. But I have no idea why it becomes empty again outside!
const getLineReader = function () {
return require('readline').createInterface({
input: require('fs').createReadStream('data.txt')
});
};
const getSystem = function () {
const lineReader = getLineReader();
const services = [];
lineReader.on('line', function (line) {
const serviceJSON = JSON.parse(line);
const tests = serviceJSON.tests.map(test => {
return new ServiceTest(
test.id,
test.name,
test.criticality);
});
const service = new NewService(new UniqueID(), serviceJSON.name, tests, new Timestamp());
services.push(service);
console.log(services); // prints Services { _services: [relevant data here] }
});
console.log(services); // prints Services { _services: [] }
You need to listen to the readline 'close' event and then print the array. close will be called once all lines have been read.
lineReader.on('close', function() {
console.log(services)
});
You'll then end up with something like:
const getSystem = function () {
const lineReader = getLineReader();
const services = [];
lineReader.on('line', function (line) {
const serviceJSON = JSON.parse(line);
const tests = serviceJSON.tests.map(test => {
return new ServiceTest(
test.id,
test.name,
test.criticality);
});
const service = new NewService(new UniqueID(), serviceJSON.name, tests, new Timestamp());
services.push(service);
console.log(services); // prints Services { _services: [relevant data here] }
});
lineReader.on('close', function() {
console.log(services)
});
}
In your current code, console.log(services) will fire before the line lineReader.on('line', ...) code.

Categories