Promise function with FileReader resolves prematurely - javascript

I'm opening a file to read contents like so:
convertBlobToBase64(blob){
var convertPromise = new Promise(function(resolve, reject){
var fileReader = new FileReader();
fileReader.onload = function() {
var dataUrl = this.result;
var base64 = dataUrl.split(',')[1];
resolve(base64);
};
fileReader.readAsDataURL(blob);
});
return convertPromise;
}
I then call this function and pass the result data when it resolves:
myFunction(audioFile){
var to64 = this.convertBlobToBase64(audioFile);
to64.then(function(base64Val){
var nextPromise = postCall();
nextPromise.then(//stuff);
return nextPromise;
});
return to64;
}
However, when I call myFunction, it immediately returns a resolved promise that includes the converted data from convertBlobToBase64, and not an unresolved promise that should be waiting on nextPromise as expected.
Instead, myFunction's .then is called immediately and fails as it doesn't have the correct data. Am I misunderstanding the Promise function?

Try this code:
myFunction(audioFile){
var to64 = this.convertBlobToBase64(audioFile);
return to64.then(function(base64Val){
var nextPromise = postCall();
return nextPromise.then(//stuff);
});
}

Btw you dont need to wrap another promise to a function. You can use your postCall as resolve func and chain it like this:
myFunction(audioFile){
return convertBlobToBase64(audioFile)
.then(base64Val => postCall())
.then(//stuff)
}

Related

how to get the value from a Promise resolve?

I am trying to read user uploaded file and convert it into String. I have 2 functions to do this.
handleFileInput
handleFileInput(event){
setTimeOut(async()=>{
let abcd= await this.convertFileToString(this.file) //the file has been uloaded successFully at this point
console.log(abcd) //this prints the enitre fn given in the resolve method
},3000)
}
convertFileToString
convertFileToString(file){
return new Promise((resolve, reject)=>{
let fileReader = new FileReader();
fileReader.readAsText(file);
resolve(fileReader.onload = (event) =>{
this.XMLAsString=fileReader.result as String
})
})
}
When i print the value of abcd in the console i get this:
ƒ (event) {
_this.XMLAsString = fileReader.result;
}
I am fairly new to the concept of async/await and Promises and understand that promise is the only asynchronous thing i can await. I want the value of the uploaded file (converted to String) to be stored in variable abcd. how do i get the value? Or if i have to return a promise, then how do i access the value of file read as String and store in in abcd?
Your convertFileToString looks a little wrong: you should be invoking resolve() in the onload handler, not the other way round:
convertFileToString(file){
return new Promise((resolve, reject)=>{
let fileReader = new FileReader();
fileReader.readAsText(file);
fileReader.onload = (event) => {
resolve(event.target.result);
}
})
}

Javascript Promise not waiting to resolve before next Then()

Anyone know why this does not run synchronically? The last promise seems to resolve before the first
...
var promise = Promise.resolve();
promise.then( () => {
return new Promise((resolve, reject) => {
var file1 = fileChooserCSV.files[0];
var reader1 = new FileReader();
reader1.onload = function(){
var csv = reader1.result;
csvJson = csvJSON(csv);
resolve();
};
reader1.readAsText(file1);
});
});
promise.then( () => {
return new Promise((resolve, reject) => {
var file2 = fileChooserConfig.files[0];
var reader2 = new FileReader();
reader2.onload = function(){
var config = reader2.result;
configJson = JSON.parse(config);
resolve();
};
reader2.readAsText(file2);
});
});
promise.then( () => {
return new Promise((resolve, reject) => {
console.log('end');
resolve();
});
});
The reader onload methods never seem to execute though they really should (there is data passed to them), and did so before they were moved to the promise. As the onload doesn't run the resolve() never fires ether to go onto the next then(), but the last then() does execute...
This code runs in a chrome extension popup if that makes any difference?
Many thanks!
UPDATE..
Restructuring it in a classic nested way works fine
var file1 = fileChooserCSV.files[0];
var reader1 = new FileReader();
reader1.onload = function(){
var csv = reader1.result;
csvJson = csvJSON(csv);
var file2 = fileChooserConfig.files[0];
var reader2 = new FileReader();
reader2.onload = function(){
var config = reader2.result;
configJson = JSON.parse(config);
console.log('end');
};
reader2.readAsText(file2);
};
reader1.readAsText(file1);
did u tryied Promise.All().then... like that
var promise = Promise.resolve(3);
Promise.all([true, promise])
.then(function(values) {
console.log(values); // [true, 3]
});
https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
In JS runtime the 'then' are moved to another stack and are executed only when the call stack is empty.
For example, here you create I create a Promise.resolve() like you. But notice it's then is executed only after the entire code was executed. You will see on you screen '53' instead of expected '35':
const promise = Promise.resolve(3)
promise.then(res => {
document.write(res)
})
document.write(5)
'then' is stored in additional stack and executed only later.
For more info take a look at this perfect explanation form Barak Chemo. Watch till 30:40.
Hope it helps

Importing / exporting only after certain promises have resolved

Let's say I have a file containing certain promises, that when executed in order, prepares an input file input.txt.
// prepareInput.js
var step1 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
var step2 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
var step3 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
step1().then(step2).then(step3);
exports.fileName = "input.txt";
If I run node prepareInput.js, the line step1().then(step2).then(step3) gets executed and creates the file.
How can I alter this so that when other files attempt to retrieve fileName from this module, step1().then(step2).then(step3); is run and completed before fileName is exposed? Something along the line of:
// prepareInput.js
...
exports.fileName =
step1().then(step2).then(step3).then(function() {
return "input.txt";
});
// main.js
var prepareInput = require("./prepareInput");
var inputFileName = require(prepareInput.fileName);
Node.js beginner here; apologize beforehand if my approach makes completely no sense... :)
You can't directly export the results of something retrieved asynchronously because export is synchronous, so it happens before any async results have been retrieved. The usual solution here is to export a method that returns a promise. The caller can then call that method and use that promise to get the desired async result.
module.exports = function() {
return step1().then(step2).then(step3).then(function() {
// based on results of above three operations,
// return the filename here
return ...;
});
}
The caller then does this:
require('yourmodule')().then(function(filename) {
// use filename here
});
One thing to keep is mind is that if any operation in a sequence of things is asynchronous, then the whole operation becomes asynchronous and no caller can then fetch the result synchronously. Some people refer to asynchronous as "contagious" in this way. So, if any part of your operation is asynchronous, then you must create an asynchronous interface for the eventual result.
You can also cache the promise so it is only ever run once per app:
module.exports = step1().then(step2).then(step3).then(function() {
// based on results of above three operations,
// return the filename here
return ...;
});
The caller then does this:
require('yourmodule').then(function(filename) {
// use filename here
});

NodeJs forEach request-promise wait for all promises before returning

Problem is I'm not able to get the promises to return anything. they.. just come empty.
Every answer I see here on SO is telling me to do just this, though for some reason this is not working. I'm at my wits end, pulling hair and smashing keyboards; Can someone pin-point my dumbness?
var q = require('q');
var request = require('request-promise'); // https://www.npmjs.com/package/request-promise
function findSynonym(searchList) {
var defer = q.defer();
var promises = [];
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=awesomekeyisawesome";
var wURL;
searchList.forEach(function(word){
wURL = url.replace('%word%',word);
promises.push(request(wURL));
});
q.all(promises).then(function(data){
console.log('after all->', data); // data is empty
defer.resolve();
});
return defer;
}
var search = ['cookie', 'performance', 'danger'];
findSynonym(search).then(function(supposedDataFromAllPromises) { // TypeError: undefined is not a function [then is not a function]
console.log('->',supposedDataFromAllPromises); // this never happens
});
You're returning the Deferred object defer, which does not have a .then method, instead of the Promise object defer.promise.
But anyway, that's the deferred antipattern, there's no need of using deferreds here. Just return the promise that Promise.all gets you:
function findSynonym(searchList) {
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=awesomekeyisawesome";
var promises = searchList.map(function(word) {
return request(url.replace('%word%', word));
});
return q.all(promises).then(function(data){
console.log('after all->', data); // data is empty
return undefined; // that's what you were resolve()ing with
});
}
So, turns out I was resolving the promises or something something. returning the q.all() worked pretty well :)
function findSynonym(searchList) {
var promises = [];
var url = "http://thesaurus.altervista.org/service.php?word=%word%&language=en_US&output=json&key=REDACTED";
var wURL;
searchList.forEach(function(word){
wURL = url.replace('%word%',word);
promises.push(request({url:wURL}));
});
return q.all(promises);
}
var search = ['cookie', 'performance', 'danger'];
findSynonym(search)
.then(function(a){
console.log('->',a);
});

promise resolve before inner promise resolved

I have a promise and I want it to resolve only when inner promise has resolved. Right now it resolves before the "resolve" function has been reached in the "loadend" callback.
What am I missing? I am confused about the way you are supposed to use resolve and about how you can use a promise within another promise.
I couldn't find anything that helped on the web.
In the following example I basically load a bunch of files, for each file I get a blob and I want to pass this blob in a file reader.
Once all files have been passed to the file reader, I want to move to the next function in the promise chain.
Right now it goes to the next function in the chain without waiting for resolve to be called.
var list = [];
var urls = this.files;
urls.forEach(function(url, i) {
list.push(
fetch(url).then(function(response) {
response.blob().then(function(buffer) {
var promise = new Promise(
function(resolve) {
var myReader = new FileReader();
myReader.addEventListener('loadend', function(e) {
// some time consuming operations
...
window.console.log('yo');
resolve('yo');
});
//start the reading process.
myReader.readAsArrayBuffer(buffer);
});
promise.then(function() {
window.console.log('smooth');
return 'smooth';
});
});
})
);
});
...
// run the promise...
Promise
.all(list)
.then(function(message){
window.console.log('so what...?');
})
.catch(function(error) {
window.console.log(error);
});
When you don't return anything from then callbacks, it assumes synchronous operation and goes to resolve the result promise with the result (undefined) immediately.
You need to return a promise from every asynchronous function, including then callbacks that you want to get chained.
Specifically, your code should become
var list = this.files.map(function(url, i) {
// ^^^^ easier than [] + forEach + push
return fetch(url).then(function(response) {
return response.blob().then(function(buffer) {
return new Promise(function(resolve) {
var myReader = new FileReader();
myReader.addEventListener('loadend', function(e) {
…
resolve('yo');
});
myReader.readAsArrayBuffer(buffer);
}).then(function() {
window.console.log('smooth');
return 'smooth';
});
})
});
});
or even better, flattened:
var list = this.files.map(function(url, i) {
return fetch(url).then(function(response) {
return response.blob();
}).then(function(buffer) {
return new Promise(function(resolve) {
var myReader = new FileReader();
myReader.addEventListener('loadend', function(e) {
…
resolve('yo');
});
myReader.readAsArrayBuffer(buffer);
});
}).then(function() {
window.console.log('smooth');
return 'smooth';
});
});

Categories

Resources