Execute a new set of fetches only after the earlier set has ran trough - javascript

I'm trying to fetch images for a live camera feed web page from an API. The problem is that with just timed fetch request, eventually one of the API responds takes so long that the code errors. The page has multiple camera feeds that are simultaneously refreshing with the loop for all the cameras in the cameraObjects[] array. The image() function should respond with a resolve that would then be collected in to an array promises[].
Put simply I need to run the refreshImages() function when ALL the image() functions called by the loop in updateImages() have been ran. I have just started coding so bare with me...
class camera {
constructor(Uuid,url,username,password) {
this.Uuid = Uuid;
this.url = url;
this.username = username;
this.password = password;
}
image() {
let uuid = this.Uuid
let url = this.url
let username = this.username
let password = this.password
let headers = new Headers();
let authString = `${username}:${password}`;
headers.set('Authorization', 'Basic ' + btoa(authString));
let imageUrl = url + uuid
fetch(imageUrl,{method: 'GET', headers: headers})
.then(response => response.blob())
.then(image => {
console.log(image);
var reader = new FileReader();
reader.readAsDataURL(image);
reader.onloadend = function() {
var base64data = reader.result;
let img = document.getElementById("camera_" + uuid);
img.src = base64data;
return new Promise(function(resolve) {
resolve(promise);
})
}
})
}
}
function updateImages() {
cameraObjects = listOfCameraObjects();
let promises = [];
for(let e = 0; e < cameraObjects.length; e++) {
let promise = new Promise(cameraObjects[e].image())
promises.push(promise)
}
Promise.all(promises)
.then(() => {
refreshImages();
})
}
function refreshImages() {
let currentInterval = getInterval();
refrehInterval = setTimeout(updateImages, currentInterval);
console.log(refrehInterval)
}

There a few things you're doing wrong with Promises -
return new Promise(function(resolve) {
resolve(promise);
})
That's sort of OK though return Promise.resolve(promise) is identical - however in your code, what is promise? not declared anywhere in that scope - also, as the last code in an event handler (onloadend) it is pointless, since returning a value from an event handler is meaningless
let promise = new Promise(cameraObjects[e].image())
that's not how you construct a promise ... the argument to the promise constructor needs to be a function, not the result of calling a function (unless that returns a function, of course, but it doesn't)
I'd suggest you perhaps read some docs about how you construct a Promise, and how you then use them
In the meantime, I believe this code will do what you want
class camera {
constructor(Uuid,url,username,password) {
this.Uuid = Uuid;
this.url = url;
this.username = username;
this.password = password;
}
image() {
const headers = new Headers();
const authString = `${this.username}:${this.password}`;
headers.set('Authorization', 'Basic ' + btoa(authString));
const imageUrl = this.url + this.Uuid;
return fetch(imageUrl, {method: 'GET', headers: headers})
.then(response => response.blob())
.then(image => new Promise((resolve, reject) {
const reader = new FileReader();
reader.readAsDataURL(image);
reader.addEventListener('loadend', () => {
const img = document.getElementById("camera_" + uuid);
img.src = reader.result;
resolve();
});
reader.addEventListener('error', reject);
}));
}
}
function updateImages() {
Promise.all(listOfCameraObjects().map(cam => cam.image()))
.then(refreshImages);
}
function refreshImages() {
let currentInterval = getInterval();
refrehInterval = setTimeout(updateImages, currentInterval);
console.log(refrehInterval)
}

Related

return true in nested function part of new Image() .onload to verify image exist from url?

How can I return true when function is run inside function part of new Image() .onload, in order to verify if a url is a valid image?
var valid = false;
checkImage('https://example.com/image.png')
console.log(valid) //always false at first run
function checkImage(url) {
var image = new Image();
image.onload = function () {
if (this.width > 0) {
valid = true;
}
}
image.onerror = function() {
valid = false;
}
image.src = url;
}
I also tried setting a global variable which doesn't work,Or any other way to return true / false back via checkImage(url) ?
Got this initial solution from https://stackoverflow.com/a/55880263/8719001
(async () => {
let valid = await checkImage('https://example.com/image.png')
console.log(valid)
})();
async function checkImage(url) {
return new Promise(resolve=>{
const image = new Image()
image.onload = () => resolve(!!image.width)
image.onerror = () => resolve(false)
image.src = url
})
}
Following your code example you'll need to wrap your result in a Promise, which is an object made for "returning a result later":
function checkImage(url) {
return new Promise((resolve, reject) => {
var image = new Image();
image.onload = function () {
if (this.width > 0) {
resolve()
} else {
reject()
}
}
image.onerror = reject
image.src = url;
})
}
const valid = await checkImage('https://example.com/image.png')
Alternatively, a simpler way of doing this would be to use fetch if your only goal is to check for the file's existence (and not necessarily checking whether it works as an image):
const exists = await fetch(url, {method: 'HEAD'})
.then(response => response.status === 200)

Tensor shape must be [-1,-1,-1,3] but was [X,XXX,XXXX,X]?

I'm new to JS (especially to Node and tfjs) and I want to convert my image into a tensor.
But whenever I try to do so, I get this error:
UnhandledPromiseRejectionWarning: Error: The shape of dict['image_tensor'] provided in model.execute(dict) must be [-1,-1,-1,3], but was [1,628,1100,4]
Here's my code:
async function gotMessage(msg) {
if(msg.content === '!object') {
const attachments = (msg.attachments).array();
const filepath = "./images/" + Date.now() + "J" + ".png";
console.log(filepath);
const imageurl = attachments[0].url;
await saveImageToDisk(imageurl,filepath)
let img_buffer = fs.readFileSync(filepath)
const img = tf.node.decodePng(img_buffer)
coco.load().then(model => {
// detect objects in the image.
model.detect(img).then(predictions => {
console.log('Predictions: ', predictions);
});
});
msg.reply('Enjoy');
msg.channel.send(attachments[0].url);
}
}
async function saveImageToDisk(url,path) {
return new Promise((resolve, reject) => {
var fullUrl = url;
var localPath = fs.createWriteStream(path);
var request = https.get(fullUrl,function(response) {
//console.log(response)
response.pipe(localPath)
response.on('end', resolve);
}).on('error', reject);
});
}
decodePng returns a 4D array. If you want to use it for training your model, just pass
expand_animations=True to decodePng. It will return a 3D array.
Tensorflow doc on decodePng

return a response from an async call in a standard for loop

After reading How do I return the response from an asynchronous call? by Felix Kling, I am still confused about how I can return a value from an asynchronous callback.
My goal: convert a static image to base64 once and store that image in indexDB until indexDB throws some kind of storage error.
I am using this async idb npm module
// init the idb store
const initIDB = async () => {
const db = await openDB('db', 1, {
upgrade(db) {
db.createObjectStore('tempStore', { keyPath: 'id', autoIncrement: true });
},
});
const tx = db.transaction('tempStore', 'readwrite');
await overloadIDB(tx.store);
await tx.done;
return true;
};
// random number generator
const getRandomArbitrary = (min, max) => Math.random() * (max - min) + min;
// function will overload the idb
const overloadIDB = async (store) => {
const imgurl = "someLocalImage.png";
const promises = [];
return toDataURL(imgurl, async (s) => {
for (let i = 0; i < 10; i++) {
if (i > 0 && i % 100 === 0) console.log('A set done');
try {
const num = Math.round(getRandomArbitrary(1, 1000000));
const data = {
id: num,
img: s,
};
store.add(data);
} catch (e) {
console.log(e.toString());
console.dir(e);
break;
}
}
console.log('Done');
});
};
// convert image to base64
const toDataURL = (url, callback) => {
const xhr = new XMLHttpRequest();
xhr.onload = () => {
const reader = new FileReader();
reader.onloadend = () => {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
};
Ideally, I would like to return the value from the toDataURL's callback function and use that result in the for loop but I always get undefined which makes sense due to asynchronous behaviour.
The above code fails to execute the transaction store.add(data) multiple times and fails when i = 0.
I have tried wrapping toDataURL with a new Promise(resolve, reject) like so
const toDataURL = (url, callback) => new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = () => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(callback(reader.result));
};
reader.readAsDataURL(xhr.response);
};
xhr.send();
});
and then using Promise.all to resolve an array of stores like so
const overloadIDB = async (store) => {
const imgurl = 'someLocalImage.png';
const promises = [];
return toDataURL(imgurl, async (s) => {
console.log('s :', s);
for (let i = 0; i < 10; i++) {
if (i > 0 && i % 100 === 0) console.log('A set done');
try {
const num = Math.round(getRandomArbitrary(1, 1000000));
const data = {
id: num,
img: s,
};
promises.push(store.add(data));
} catch (e) {
console.log(e.toString());
console.dir(e);
break;
}
}
await Promise.all(promises);
console.log('Done');
});
};
but returns an error Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.
At this point I think I my approach is flawed but I am not sure how I can fix it. Can anyone point to some solution please?
You cannot perform async operations in the middle of indexedDB operations. Perform your fetch entirely, then connect, create a transaction, and store the result.

Make ts/js-library promise based

I've implemented this file reader into my project.
I would like to make this return a promise when it finishes with the file reading, but I don't know how to propagate the promise from there.
class MyClass {
constructor() {}
public start(file) {
parseFile(file);
}
private parseFile(file) {
let fileSize = file.size;
let chunkSize = 10000;
let offset = 0;
let self = this;
let readBlock = null;
// How do I get this success function to return a promise to the user?
let success = function() { return new Promise...? };
let onLoadHandler = function(evt) {
if (evt.target.error == null) {
offset += evt.target.result.length;
chunkReadCallback(evt.target.result);
} else {
chunkErrorCallback(evt.target.error);
return;
}
if (offset >= fileSize) {
success(file);
return;
}
readBlock(offset, chunkSize, file);
}
readBlock = function(_offset, length, _file) {
let r = new FileReader();
let blob = _file.slice(_offset, length + _offset);
r.onload = onLoadHandler;
r.readAsText(blob);
}
readBlock(offset, chunkSize, file);
}
}
Today it works like this:
let x = new MyClass();
x.start(file);
And I would like it to be like this instead:
let x = new MyClass();
x.start(file).then(() => { console.log('done') });
Where do I put my return Promise so that the user can handle the promise?
Thanks!
The following should turn readFile into a promise:
private parseFile(file,chunkSize,offset) {
let fileSize = file.size;
let self = this;
readBlock = function (_offset, length, _file) {
return new Promise(
function(resolve,reject){
let r = new FileReader();
let blob = _file.slice(_offset, length + _offset);
//https://developer.mozilla.org/en-US/docs/Web/API/FileReader/onload
r.onload = function(e){
if(e.target.error!==null){
reject(e.target.error);
}
else{
resolve(e.target.result)
}
};
//https://developer.mozilla.org/en-US/docs/Web/API/FileReader/onerror
r.onerror = function(err){
reject(err);
}
r.readAsText(blob);
}
)
}
return readBlock(offset, chunkSize, file);
}
You can have the caller define what the block size is and when to read the next block.
An example how to use this function:
x.parseFile(file,file.size,0)
.then(
function(textData){
console.log(textData);
}
);
//read in chunks of 1000
function readInChunks(file,chunkSize=1000,offset=0,data=""){
return x.parseFile(file,chunkSize,offset)
.then(
function(textData){
if(offset+chunkSize>=file.size){
return data+textData;
}
console.log("get next chunk");
//recursively call itself
return readInChunks(file,chunkSize,offset+chunkSize,data+textData);
}
)
}
//call read in chunks
readInChunks(file,/* optional, defaults to 1000 */500)
.then(
function(textData){
console.log("got data:",textData);
}
)

How to wait for forEach to complete when each iteration calls an asynchronous options?

Alright, here's what the plan is. Go through each file, add the file into the array. Once all files are added, then combine them using the JSZipUtility and Docxtemplater:
'click .merge-icon': (e) => {
var programId = Router.current().url.split('/').pop();
var programObj = Programs.findOne(programId);
var insertedDocuments = [];
var i = 0;
var count = programObj.activityIds.count;
var fileDownloadPromise = new Promise((resolve, reject) => {
programObj.activityIds.forEach(function(activityId) {
var activityObj = Activities.findOne(activityId);
var documentObj = ActivityFiles.findOne(activityObj.documents.pop()._id);
JSZipUtils.getBinaryContent(documentObj.url(), callback);
function callback(error, content) {
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
var xml = zip.files[doc.fileTypeConfig.textPath].asText();
xml = xml.substring(xml.indexOf("<w:body>") + 8);
xml = xml.substring(0, xml.indexOf("</w:body>"));
xml = xml.substring(0, xml.indexOf("<w:sectPr"));
insertedDocuments.push(xml);
i++;
if (i == count - 1) {
resolve();
}
}
});
});
fileDownloadPromise.then(() => {
JSZipUtils.getBinaryContent('/assets/template.docx', callback);
function callback(error, content) {
console.log(content);
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
setData(doc);
}
function setData(doc) {
doc.setData({
body: insertedDocuments.join('<w:br/><w:br/>')
});
doc.render();
useResult(doc);
}
function useResult(doc) {
var out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
saveAs(out, programObj.name + '.docx');
}
});
}
Turns out nothing's happening. What's wrong with the execution of this Promise here ?
I'm only calling resolve when every file has been loaded in the array.

Categories

Resources