Assigning JS promises value to a variable - javascript

As a starter in JS promises it is a bit confusing to me, I am trying to get a base64 of an image using this function :
const convertFileToBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
})
when I call this function I get a promise I can use .then to print the value, but I cannot assign the returned value of the function to a variable .

When calling a function that returns a Promise there are 2 ways to get the result from it:
using .then
using async/await
.then approach:
convertFileToBase64(myFile)
.then(result => {
// do something with the result within this little function
})
.catch(err => {
// handle any error
});
async/await approach:
// must be inside an async function
async function doSomething() {
const result = await convertFileToBase64(myFile); // wait and assign result to variable
// do something with the result
// errors can get handled by whoever calls this async function
}

Related

How javascript Promise works

I have this script that get random videos in the web.
The function getRandomVideo() returns a Promise with a list of urls.
What I want to do is print the data by calling main().then(data => console.log(data)). The problem is that data is being printed before the function is done running. So when I start the program I get undefined and then after the function is done I get the actual data.
I thought that what is inside .then() would run just after the promise is returned.
Does anyone know what is happening?
const main = async () => {
let allData = [];
getRandomVideo().then((videoLinks) => {
allData = videoLinks;
return new Promise((resolve) => {
resolve(allData);
});
});
};
main().then((data) => console.log(data));
As others have said, your code is full of anti-patterns in an attempt to avoid the basics of asynchronous programming with promises. All you need is this:
const main = function() => {
return getRandomVideo();
}
main().then(data => console.log(data)).catch(err => console.log(err));
Or, of course, you don't even need main() at all. You can just do:
getRandomVideo().then(data => console.log(data)).catch(err => console.log(err));
Some anti-patterns in your original code:
Not returning anything from your main() function.
Assigning an asynchronous value to a higher scoped variable and hoping that it somehow fixes asynchronous programming issues. It is nearly always a warning that you're doing something wrong when you assign an asynchronously retrieved value to a higher scoped variable because the code in the higher scope generally has no idea when that value will actually be available. Only within the .then() handler so you know when the value is present.
Manually creating a promise for no particular reason.
Marking a function async for no reason.
No error handling (e.g. .catch() handler) when calling a function that returns a promise.
If you want to manipulate the results of getRandomVideo before you send it back to the caller, then you could have a reason to use async/await
const main = async () => {
let videos = await getRandomVideo();
// define some filter function for the videos array and
// return the filtered result as the resolved value
return videos.filter(...);
}
main().then(data => console.log(data)).catch(err => console.log(err));
I think we can understand main function step by step:
let allData = []; //Defined allData variable
call getRandomVideo function
The thread wait getRandomVideo completed and return videoLinks array when you call .then function
allData = videoLinks; //Asign videoLinks array to allData variable
return new Promise((resolve) => { resolve(allData) }) //Return a Promise, that will tell the thread that you will do that later, on the other hand, after few second it's will comback and return all data by call resolve function
End of main function
==>> So as you can see, main function don't return a Promise imediately, you can't use .then to wait until it completed
Solution: Let wrap main function detail by a Promise like that
const main= () => {
return new Promise((resolve) => {
getRandomVideo().then((videoLinks) => {
resolve(videoLinks);
});
});
}
And clean your code by drop allData variable.

Use async/await instead of promise to handle loading an image in Javascript [duplicate]

This question already has answers here:
How to turn this callback into a promise using async/await?
(2 answers)
Closed 3 years ago.
I have written a promise based loading image function.
function loadImage() {
const img = new Image();
return new Promise((resolve, reject) => {
img.addEventListener("load", () => {
resolve();
});
img.addEventListener("error", () => {
reject();
});
img.src = 'assets/myImg.jpg';
});
}
loadImage.then(
() => console.log('image loaded'),
() => console.error('image did not load')
);
I would like to convert it using the async/await but I am actually struggling to do it. Any idea how it can achieved ?
You can await your loadImage function, but the new Image() object doesn't use promises, it uses events which you've wrapped with a Promise.
Inside of an async function, you can await your loadImage function, like so:
async function loadImage() {
const img = new Image();
return new Promise((resolve, reject) => {
img.addEventListener("load", () => {
resolve();
});
img.addEventListener("error", () => {
reject();
});
img.src = 'assets/myImg.jpg';
});
}
async function doSomething() {
try {
await loadImage();
console.log("image loaded");
} catch(e) {
console.error("image did not load");
}
}
With a couple changes you can make it work.
The async await side of things would generally replace the ".then()" chaining.
First of all, keep the loadImage() function as is, and define an additional "async" function to run your async await code in. I've called this "main()". You can then create a variable, for example "loadedImage", and then await the promise function. This will pause execution of the next step (being the console log in this case) until the promise has resolved/rejected and the image is loaded.
async function main() {
let loadedImage = await loadImage();
console.log(loadedImage);
}
main()
Additionally, if you wanted to actually return a value into the loadedImage variable (as right now it will be undefined), inside your promise "resolve()" you can place a value. Eg:
resolve('the image has loaded')

Why await a promise doesn't wait the promise to resolve?

I am trying to learn to use properly async await but I am kind of cofused about it.
In the snippets, I am trying to build an array of object containing the infos I need about the file I am uploading in the component. The problem is that the objects in this.fileInfo are not exactly waiting the promise to return the encoded images, returning this output as I console.log this.fileInfo:
As you can see, the key image is a ZoneAwarePromise whose value is undefined. Can you please help me to fix this?
Function build()
async build(e) {
let files = e.target.files;
this.j = Array.from(files);
this.fileInfo = await this.j.reduce((acc, cur) => [
...acc, {
name: cur.name.replace(/^.*\\/, ""),
sizeunit: this.getSize(cur.size),
extention: cur.name.split(/\.(?=[^\.]+$)/).slice(-1).pop().toString(),
image: this.previewFile(cur)
}
], [])
console.log(await this.fileInfo);
}
Promise
async previewFile(file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
return new Promise((res) => {
res(reader.result)
}).then( res => res);
};
}
You are not awaiting anything in this function: async previewFile(file).
Perhaps you assume returning a new Promise somewhere along the lines of your code will make it function as a Promise. In this particular case it will not work, because it is inside a delegate (onload), that will not be executed within the scope of your function previewFile().
You can lose the async modifier, because you can return a Promise instead:
previewFileAsync(file) {
// the async modifier keyword is not necessary,
// because we don't need to await anything.
return new Promise((res) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => res(reader.result);
});
}
When you call this, you can await it inside your loop:
async buildAsync(e) {
let files = e.target.files;
for(let i = 0; i < files.length; i++) {
const file = files[i];
const preview = await previewFileAsync(file);
// Do something with preview here...
}
}
Of course, you can execute a range of promises to allow for some sort of concurrency, but this will help get you started.
I added the Async suffix to your method so a caller knows that this can be awaited. It does not do anything special, but it helps clarify your code. You can use whatever suffix you think is right. I'm just used to the Async suffix.
Edit
Stackblitz example of async logic

Correct way to sequence two asynchronous operations that each return a promise javascript

I was wondering what is the proper way to call a promise after another promise resolves. I know we can use async await to create functions which will resolve a promise. I was wondering which form of handling promises is consider proper practice, or is it good practice to create generators instead? consider the following code:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value')), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value dueeee')), 3000);
});
const get = () => {
return fetchSomething().then(function(){
fetchSomethingElse()
});
}
get();
or
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve({resolve: true}), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve({resolve: true}), 3000);
});
const get = async function() {
const fet = await fetchSomething();
const fet2 = await fetchSomethingElse();
};
get();
Either one is fine. Your choice.
In the first you're nesting .then() handlers. In the second, you're using the newer await to sequence them. More people are moving to await because it appears to be simpler code for sequencing operations (assuming you do proper error handling), though in this case, they are pretty similar in complexity, particularly with the simplification suggested below so it's really up to your own personal coding style.
What is missing in both is that get() just returned a promise so you need to use .then() and .catch() with it to get the value and catch any errors.
Also, something that is missing in the first is that you aren't returning the second promise which means the caller won't know when the second operation is done.
Your first can be simplified and fixed up like this:
const get = () => {
return fetchSomething().then(fetchSomethingElse);
}
get().then(val => {
// done here
}).catch(err => {
// error here
});
As Pointy mentioned, you don't "call a promise". You "call a function that returns a promise". Promises are objects. They are not callable.
Probably what your title could be rewritten to is: "Correct way to sequence two asynchronous operations that each return a promise".
For completeness, if your two async operations don't depend upon one another, then you don't have to manually sequence them. You can start them both and then monitor when both are done. This will sometimes get a faster end-to-end response.
You can do that using Promise.all():
const get = function() {
return Promise.all([fetchSomething(), fetchSomethingElse()]).then(results => {
// process results array and then return final value
// results[0] is result from fetchSomething, results[1] is result from fetchSomethingElse
return finalVal;
});
}
Both are fine, but you are making a common mistake in the top example (and maybe it's just because of the simplification of the code for the question). You are returning the promise from get, but you are not returning the promise from the then. This means the caller of get won't know when both promises have resolved. Consider:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value')), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value dueeee')), 3000);
});
const get = () => {
return fetchSomething().then(function(){
fetchSomethingElse()
});
}
// we don't when fetchSomethingElse is done
get().then(() => console.log("done"));
Also there's another option you might consider since the second promise doesn't depend on the output of the first. Call them in parallel:
const get = () => {
return Promise.all([fetchSomething(), fetchSomethingElse() ])
}
In this case one can start before the other is finished and the whole operation should be faster.
It's important to remember that in Promise-based patterns you're using functions that return Promises. Promises are passed in resolve and reject arguments (which are themselves functions). What you resolve with, is what gets exectuted in the .then() phase, and what you reject with gets exectuted in the .catch() phase.
To handle Promises in sequence, you're passing your values into the top-level function that wraps the Promise.
so...
const p1 = () => {
return new Promise((resolve,reject) => {
window.setTimeout(() => {
resolve('future value one');
},500);
});
};
const p2 = (v1) => {
return new Promise((resolve,reject) => {
window.setTimeout(() => {
const v2 = 'future value two';
resolve({v1,v2});
},500);
});
};
p1().then(p2).then(console.log);

How to get data returned from fetch() promise?

I am having trouble wrapping my head around returning json data from a fetch() call in one function, and storing that result in a variable inside of another function. Here is where I am making the fetch() call to my API:
function checkUserHosting(hostEmail, callback) {
fetch('http://localhost:3001/activities/' + hostEmail)
.then((response) => {
response.json().then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
});
}
And this is how I am trying to get the returned result:
function getActivity() {
jsonData = activitiesActions.checkUserHosting(theEmail)
}
However, jsonData is always undefined here (which I am assuming is because the async fetch call has not finished yet when attempting to assign the returned value to jsonData. How do I wait long enough for the data to be returned from the fetch call so that I can properly store it inside of jsonData?
always return the promises too if you want it to work:
- checkUserHosting should return a promise
- in your case it return a promise which return the result data.
function checkUserHosting(hostEmail, callback) {
return fetch('http://localhost:3001/activities/' + hostEmail)
.then((response) => {
return response.json().then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
});
}
and capture it inside .then() in your main code:
function getActivity() {
let jsonData;
activitiesActions.checkUserHosting(theEmail).then((data) => {
jsonData = data;
}
}
EDIT:
Or even better, use the new syntax as #Senthil Balaji suggested:
const checkUserHosting = async (hostEmail, callback) => {
let hostEmailData = await fetch(`http://localhost:3001/activities/${hostEmail}`)
//use string literals
let hostEmailJson = await hostEmailData.json();
return hostEmailJson;
}
const getActivity = async () => {
let jsonData = await activitiesActions.checkUserHosting(theEmail);
//now you can directly use jsonData
}
You're partially right. It's because you're trying to get the result of this asynchronous call in a synchronous fashion. The only way to do this is the same way you deal with any other promise. Via a .then callback. So for your snippet:
function getActivity() {
return activitiesActions.checkUserHosting(theEmail).then((jsonData) => {
// Do things with jsonData
})
}
Any function that depends on an asynchronous operation must itself become asynchronous. So there's no escaping the use of .then for anything that requires the use of the checkUserHosting function.
You can make use of new ES6 and Es7 syntax and what others have written is also correct, but this can be more readable and clean,
you are trying to get aysnc value synchronously, here jsonData will be undefined because, you move to next line of execution before async function(checkUserHosting) is finish executing, this can be written as follows
const getActivity = async () => {
let jsonData = await activitiesActions.checkUserHosting(theEmail);
//now you can directly use jsonData
}
and you can write checkUserHosting in a different using new syntax like this
const checkUserHosting = async (hostEmail, callback) => {
let hostEmailData = await fetch(`http://localhost:3001/activities/${hostEmail}`)
//use string literals
let hostEmailJson = await hostEmailData.json();
return hostEmailJson;
}

Categories