How to call a function inside bluebirdjs promise - javascript

I am trying to get a promise function with bluebirdjs. but all attempts falling my way because maybe I don't know what I am doing...?
I want to get files locations, then download the files one after the other then push to an array.
import * as Promise from 'bluebird';
fileFunction(files){
Promise.map(files, function(file) {
// Promise.map awaits for returned promises as well.
//Promise.delay(1000);
return this.getLocation(file);
},
).then((file) => {
console.log('done')
});
}
getLocation(file){
if(file){
return this._storage.ref(file).getDownloadURL().subscribe(url =>
this.img_array.push(url)
);
}
When I call the return this.getLocation(file)... I get the following error
bluebird.js:1545 Unhandled rejection TypeError: Cannot read property 'getLocation' of undefined ....bluebird.js
Edit part of the code am using now!
fileFunction(files){
return Promise.map(files, file => {
return this.getDownloadUrl(file);
}).then(locations => {
// add these locations onto this.img_array
this.img_array.push(...locations);
console.log('done');
return locations;
});
}
getFiles(e): Promise<any>{
this.outPutFiles = e;
this.fileFunction(this.outPutFiles).then(locations => {
locations.map((arr) => arr.subscribe((files) => this.downloadUrls.push(files)));
}).catch(err => {
console.log(err);
});
}
getDownloadUrl(file){
if(file){
return this._storage.ref(file).getDownloadURL();
} else {
return Promise.reject(new Error('No file passed to getLocation'));
}
}

this.getLocation(file) does not work because you've lost the value of this because you're inside a Promise.map() callback. Remember, that every normal function call in Javascript changes the value of this unless you specifically control the value of this.
You can fix that part of the issue with a simple arrow function for your callback like this:
fileFunction(files){
return Promise.map(files, file => {
return this.getLocation(file);
}).then(locations => {
// add these locations onto this.img_array
this.img_array.push(...locations);
console.log('done');
return locations;
});
}
This assumes that this.getLocation(file) returns a promise that resolves to the location value. Are you sure it does that? It looks like there may be more to your problem than just that first error you ran into.
And, after a side conversation, you also need to fix getLocation() to return a promise that resolves to the desired URL. Looking in the firebase Javascript doc, it appears that getDownloadURL() already returns a promise that resolves to the desired URL. So, you can just return that promise and let Promise.map() manage the results for you.
getLocation(file){
if(file){
return this._storage.ref(file).getDownloadURL();
} else {
return Promise.reject(new Error("No file passed to getLocation"));
}
}
And, then you would use it all like this:
obj.fileFunction(fileArray).then(locations => {
console.log(locations);
}).catch(err => {
console.log(err);
});

Related

Different behaviour between 'returning a promise' and 'resolving with promise' inside a `new`-generated promise

It's not much I discovered Javascript Promise.
However I found out a behaviour I couldn't understand concerning the nesting of (new or returned) Promise inside Promises
That's the background (extPromiseX is a third-party function returning Promise):
Case 1:
function myAction(param) {
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
});
}
// Main Process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('Exit with error', error);
});
Now, as expected, I got from the console, in
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
4) Process Terminated
Case 2:
function myAction(param) {
return new Promise(function() {
if (itsAllOkWithInputs) {
// Some sync code here
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
})
} else {
throw 'Something went wrong';
}
});
}
// Main process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('3) --> Exit with error', error);
})
In this second case extPromises are executed but the very first Promise remains pending (confirmed by debug). So the console shows:
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
I empirically realized that I had to change myAction function as follow for the code to work:
function myAction(param) {
return new Promise(function(resolve, reject) {
if (itsAllOkWithInputs) {
// Some sync code here
let ep = extPromise1(sql).then(...);
resolve(ep);
} else {
throw 'Something went wrong';
}
});
}
My question:
I thought returning a promise inside another parental one would make
the parent resolving with the results of the child. This is the case
inside the then code block applied to the external promises. Why
this is not valid for the new Promise case?
Because .then is meant to chain promises. You can return a Promise from inside the then callback, and then itself will return a new Promise.
The Promise constructor is supposed to construct a Promise from an underlying callback. If you return a Promise from inside a Promise constructor, you are doing something wrong conceptually. And that's why it does not work.
function myAction(param) {
if (itsAllOkWithInputs) {
return extPromise1(sql).then(...);
} else {
return Promise.reject('Something went wrong');
}
}
// OR
async function myAction(param) {
if (itsAllOkWithInputs) {
await extPromise1(sql);
} else {
throw 'Something went wrong';
}
}
new Promise(function() {
You are not using neither resolve nor reject arguments (you actually even haven't declared them), so the promise will never get resolved or rejected. All other stuff is irrelevant.
If your function returns promise, you have to simply call it and return a result. If you don't know whether the function return promise or just a value, you can wrap it's call into Promise.resolve, but not in new Promise.

Method to stop an Await from executing if there's an empty Object from Promise but shouldn't return an error and execute other tasks

So what I want to achieve is that when a function returns an empty Object from the Promise, the Await function must not be executed and the rest of the Application must carry on executing other tasks. As the object that is returned maybe not always be available but should be returned when available.
function getData(Data) : Promise<Object> {
return new Promise((resolve, reject) => {
request({
// Method
}, (err, resp, file)=> {
if (err) {
reject(err);
} else {
resolve({
// Return object infomation
});
}
});
});
}
let someData = await Promise.all(data.map(getData));
// This should have a part that ignores if getData is empty and this await function ignored.
The rest of the application should be able to run as normal. I have tried to use:
.catch(error => { });
But didn't work the way I wanted it to work
There may be a better way, but what solved my issue was to pass an empty array to the data
if (isNullOrUndefined(data)) {
data = [];
}
In this way the await function now works the way I want it to work and does not throw error:
TypeError: Cannot read property 'map' of undefined

Improved way to deal with callbacks inside promises

I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...

How to send value from Promise 'then' to 'catch'?

I just want to ask how should I pass the resolve promise to catch if the value on the resolve is not intended.
e.g.
let prom = getPromise();
prom.then(value => {
if (value.notIWant) {
// Send to catch <-- my question is here, I want to pass it on the catch.
}
// Process data.
}).catch(err => {
// Pass the error through ipc using json, for logging.
});
I tried to using throw but the object cant be parsed to json and just got an empty object.
ANSWER:
#BohdanKhodakivskyi first comment below is the answer I want.
#31py answer is also correct but the #BohdanKhodakivskyi solution is much simplier and will render the same result.
Simply use throw value;. In your case:
prom.then(value => {
if (value.notIWant) {
// Send to catch
throw value;
}
// Process data.
}).catch(err => {
// Pass the error through ipc using json, for logging.
});
Please also note the difference and limitations between using Promise.reject() and throw which is perfectly described in this question. For example, throw will not work in some async scenarios.
You can simply return a rejected promise:
prom.then(value => {
if (value.notIWant) {
return Promise.reject('your custom error or object');
}
// Process data.
}).catch(err => {
console.log(err); // prints 'your custom error or object'
});
.catch actually handles any promise rejection in the chain, so if you're returning a rejected promise, the control automatically flows to catch.
why you just not rethrow the error? throw new Error("something");
You can use outside functions to do it:
var processData = function(data) {
// process data here
}
var logIt = function(data) {
// do logging here..
}
let prom = getPromise();
prom.then(value => {
if (value.notIWant) {
// Send to catch <-- my question is here, I want to pass it on the catch.
logIt(/*pass any thing*/);
}
// Process data.
processData(data);
}).catch(err => {
logIt(/*pass any thing*/);
});

How to get function return result?

I am developing a file reading service that look like this:
angular.factory('fileService', fileService);
function fileService($cordovaFile){
var service = {
readFile: readFile
};
return service;
///////////////
function readFile(path, file){
$cordovaFile.readAsText(path, file)
.then(function (success) {
console.log("read file success");
console.log(success);
return success;
}, function (error) {
alert("Fail to read file:"+error);
console.log("Fail to read file");
console.log(error);
return false;
});
}
}
And then using it like this:
var data = fileService.readFile(cordova.file.dataDirectory,filename);
console.log(data) //return undefined
The problem is it fail to return the data. How can I get the data return back?
Your problem is that you are not actually returning any result from the readFile function. You are returning data from your callback functions but if you come to think of it...that result is returned to the function readFile itself and it stays inside that function. What you would want to do is return the whole result of the function readFile and then resolve the promise in the controller where you use it. Here is the code:
angular.factory('fileService', fileService);
function fileService($cordovaFile){
var service = {
readFile: readFile
};
return service;
function readFile(path, file){
return $cordovaFile.readAsText(path, file);
}
}
And then you use it like this:
var data = fileService.readFile(cordova.file.dataDirectory,filename);
data.then(function (success) {
// Do whatever you need to do with the result
}, function (error) {
/// Handle errors
});
In general, when you use services to implement some kind of functionality that uses promises and returns result, you should always return the promise object which can be than resolved anywhere that it is needed.
I highly recommend that you read this great explanation for promise objects.
Your function readFile returns nothing, so, firstly you should be returning the promise:
function readFile(path, file) {
return
$cordovaFile.readAsText(path, file).then(function (success) {
console.log('Read file success');
console.log(success);
return success;
}, function (error) {
alert('Fail to read file: ' + error);
console.log('Fail to read file');
console.log(error);
return false;
});
}
And then, if you try to use it the way you were, you'll not get undefined anymore, you'll get a promise.
But since it's an async method, you'll get that promise still pending, and you probably don't want that, since you'll need the promise's fulfilled value. So, you should use it like this:
fileService.readFile(cordova.file.dataDirectory, filename).then(function(data) {
// use data here
});

Categories