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
});
Related
pardon me for asking such a beginner level question, because I not satisfied with answers titled with same question. Basically I need a promise return type for my function after performing some packages function which also returns promise.
myquest.js
module.exports = somefunction = (data){
//performs some processing with data
somePackagePromiseFunc() //return type promise
.then((data) => {
console.log(data);
return new Promise.resolve(data);
}).catch( (err) => {
console.log(err);
return new Promise.reject(err);
});
}
mymain.js
var somefunction = require('myquest');
somefunction(data).then((data) => {
console.log('job done with data ' + data);
}).catch(() => {
console.log('we messed with error: ' + err);
})
Help me to understand my mistake.
The simplest fix to your code is
Fix the syntax in the first line
return something, in this case a Promise, in that function
is somePackagePromiseFunc a function? then call it
fix the return value in .then/.catch
You'll end up with
module.exports = function (data) {
//performs some processing with data
return somePackagePromiseFunc().then((data) => {
console.log(data);
return data;
}).catch( (err) => {
console.log(err);
throw err;
});
}
Note about some of your code:
return new Promise.resolve(data);
Promise.resolve is not a constructor, so remove new would make it
return Promise.resolve(data);
However, you're inside a .then, whatever you return is a Promise that resolves the the value returned in .then - so, no need to wrap it in Promise.resolve at all - so that's why you only need
return data;
Similarly for .catch, except to return a rejected promise, you throw instead of return - though technically you can
return Promise.reject(err);
Note, no "new", because it's also not a constructor
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);
});
I don't understand below part
var q = require("q"),
BlogPost = require("../models/blogPost");
module.exports = {
getAllPosts: getAllPosts
};
function getAllPosts() {
var deferred = q.defer();
BlogPost
.find({})
.sort("-date")
.exec(function(error, posts) {
if (error) {
deferred.reject(error);
} else {
deferred.resolve(posts);
}
});
return deferred.promise;
}
I found above code in controller, but can't understand it. Why at the end we use return deferred.promise? How would I use the getAllPosts later? Can't we just return the posts objects?
You would consume a function that returns a promise like so:
var getAllPosts = require('./your-module-name.js').getAllPosts;
getAllPosts()
.then(function(posts) {
//successfully process the posts
})
.catch(function(err) {
//handle the error
})
.finally(function(){
//clean up anything you need to...
})
Promise is the just representation of the asynchronous result.
It has three states:
1-> success
-> Error
-> Pending
deferred is a just object of promise and it returns after processing one of above state.
We can use javascript code without promise but sometimes we have to give it to make our code execute as asynchronous way.
That's why we use promise
I am trying to work through JS Promises in node.js and don't get the solution for passing promises between different function.
The task
For a main logic, I need to get a json object of items from a REST API. The API handling itself is located in a api.js file.
The request to the API inthere is made through the request-promise module. I have a private makeRequest function and public helper functions, like API.getItems().
The main logic in index.js needs to wait for the API function until it can be executed.
Questions
The promise passing kind of works, but I am not sure if this is more than a coincidence. Is it correct to return a Promise which returns the responses in makeRequest?
Do I really need all the promises to make the main logic work only after waiting for the items to be setup? Is there a simpler way?
I still need to figure out, how to best handle errors from a) the makeRequest and b) the getItems functions. What's the best practice with Promises therefor? Passing Error objects?
Here is the Code that I came up with right now:
// index.js
var API = require('./lib/api');
var items;
function mainLogic() {
if (items instanceof Error) {
console.log("No items present. Stopping main logic.");
return;
}
// ... do something with items
}
API.getItems().then(function (response) {
if (response) {
console.log(response);
items = response;
mainLogic();
}
}, function (err) {
console.log(err);
});
api.js
// ./lib/api.js
var request = require('request-promise');
// constructor
var API = function () {
var api = this;
api.endpoint = "https://api.example.com/v1";
//...
};
API.prototype.getItems = function () {
var api = this;
var endpoint = '/items';
return new Promise(function (resolve, reject) {
var request = makeRequest(api, endpoint).then(function (response) {
if (200 === response.statusCode) {
resolve(response.body.items);
}
}, function (err) {
reject(false);
});
});
};
function makeRequest(api, endpoint) {
var url = api.endpoint + endpoint;
var options = {
method: 'GET',
uri: url,
body: {},
headers: {},
simple: false,
resolveWithFullResponse: true,
json: true
};
return request(options)
.then(function (response) {
console.log(response.body);
return response;
})
.catch(function (err) {
return Error(err);
});
}
module.exports = new API();
Some more background:
At first I started to make API request with the request module, that works with callbacks. Since these were called async, the items never made it to the main logic and I used to handle it with Promises.
You are missing two things here:
That you can chain promises directly and
the way promise error handling works.
You can change the return statement in makeRequest() to:
return request(options);
Since makeRequest() returns a promise, you can reuse it in getItems() and you don't have to create a new promise explicitly. The .then() function already does this for you:
return makeRequest(api, endpoint)
.then(function (response) {
if (200 === response.statusCode) {
return response.body.items;
}
else {
// throw an exception or call Promise.reject() with a proper error
}
});
If the promise returned by makeRequest() was rejected and you don't handle rejection -- like in the above code --, the promise returned by .then() will also be rejected. You can compare the behaviour to exceptions. If you don't catch one, it bubbles up the callstack.
Finally, in index.js you should use getItems() like this:
API.getItems().then(function (response) {
// Here you are sure that everything worked. No additional checks required.
// Whatever you want to do with the response, do it here.
// Don't assign response to another variable outside of this scope.
// If processing the response is complex, rather pass it to another
// function directly.
}, function (err) {
// handle the error
});
I recommend this blog post to better understand the concept of promises:
https://blog.domenic.me/youre-missing-the-point-of-promises/
I have a function which creates a deferred object. On fail I'm calling a fallback function, which in turn creates and returns it's own deferred/promise object. I would like to return the result of this fallback-deferred but I'm only able to return the error on my initial call.
Here is what I'm doing:
// method call
init.fetchConfigurationFile(
storage,
"gadgets",
gadget.getAttribute("data-gadget-id"),
pointer
).then(function(fragment) {
console.log("gotcha");
console.log(fragment);
}).fail(function(error_should_be_fragment) {
console.log("gotcha not");
console.log(error_should_be_fragment);
});
My fetchConfiguration call tries to load from localstorage and falls back to loading from file if the document/attachment I need is not in localstorage.
init.fetchConfigurationFile = function (storage, file, attachment, run) {
return storage.getAttachment({"_id": file, "_attachment": attachment})
.then(function (response) {
return jIO.util.readBlobAsText(response.data);
})
.then(function (answer) {
return run(JSON.parse(answer.target.result))
})
.fail(function (error) {
// PROBLEM
console.log(error);
if (error.status === 404 && error.id === file) {
return init.getFromDisk(storage, file, attachment, run);
}
});
};
My problem is I can catch the 404 allright, but instead of returning the error object, I would like to return the promise generated by init.getFromDisk.
Question:
Is it possible to return the result of my getFromDisk call in the error handler? If not, how would I have to structure my calls so that I'm always returning a promise to my first method call?
Thanks for help!
SOLUTION:
Thanks for the help! Fixed it like this:
init.fetchConfigurationFile(
storage,
"gadgets",
gadget.getAttribute("data-gadget-id"),
pointer
).always(function(fragment) {
console.log("gotcha");
console.log(fragment);
});
init.fetchConfigurationFile = function (storage, file, attachment, run) {
return storage.getAttachment({"_id": file, "_attachment": attachment})
.then(function (response) {
return jIO.util.readBlobAsText(response.data);
})
.then(
function (answer) {
return run(JSON.parse(answer.target.result));
},
function (error) {
if (error.status === 404 && error.id === file) {
return init.getFromDisk(storage, file, attachment, run);
}
}
);
};
.fail() always returns the original promise.
You should call then() with a failure callback to allow chaining:
.then(undefined, function(error) {
return ...;
});
Before jQuery 1.8, use .pipe() instead.