I've been trying to convert my promise syntax from then/catch to async/await and for some reason it's now unable to return my promise.
This is the then/catch version which returns the data perfectly fine
let lotFiles = []
export function returnData(key) {
getContentWithKey(key)
.then(content => {
if (content.previousHash === '') {
lotFiles.push(content)
return lotFiles
}
lotFiles.push(content)
returnData(content.previousHash)
})
.catch(err => {
console.log(err);
})
}
This is the async/await version which doesn't return anything at all
let lotFiles = []
async function returnData(key) {
try {
let content = await getContentWithKey(key)
if (content.previousHash === '') {
lotFiles.push(content)
return lotFiles
} else {
lotFiles.push(content)
returnData(content.previousHash)
}
} catch (e) {
console.log(e);
}
}
I have another function that calls returnData -
async function returnContent(data) {
let something = await getContent(data)
console.log(something)
}
returnContent()
async/await requires a promise chain.
The returnData() function is recursive so you can place the inner most result in an array and push the other results in the chain.
async function returnData(key) {
try {
const content = await getContentWithKey(key)
if (content.previousHash === '') {
// termination of recursion
// resolve with an array containing the content
return Promise.resolve([content])
}
else {
return returnData(content.previousHash).then(function(result) {
// append the result and pass the array back up the chain
return [content].concat(result)
})
}
}
catch(error) {
return Promise.reject(error)
}
}
You can replace the inner promise chain with await.
async function returnData(key) {
try {
const content = await getContentWithKey(key)
if (content.previousHash === '') {
// termination of recursion
// resolve with an array containing the content
return Promise.resolve([content])
}
else {
try {
let result = await returnData(content.previousHash)
// append the result and pass the new array back up the chain
return [content].concat(result)
}
catch(error) {
return Promise.reject(error)
}
}
}
catch(error) {
return Promise.reject(error)
}
}
Related
I'd like some help please as I'm quite new in node.js and working with node packages.
I'm having the following script which makes a GET http request running on node using request which is deprecated now
const foo = (bar, callback) => {
const url = 'https://some.api.com?key=abc123';
request({url: url, json: true}, (error, response) => {
if (error) {
callback('Oops, there is an error!', undefined);
} else if(response.body.foobarArray.length === 0) {
callback('No data found', undefined);
} else {
callback(undefined, {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
})
}
});
}
console.log(foo('Hello')); // this logs {foobar1: 'Hello', foobar2: 'World'}
I'm trying to rewrite it using axios instead, so this is my code
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
const response = await axios.get(url);
if (response.body.foobarArray.length === 0) {
return 'No data found';
} else {
return {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
};
}
} catch (error) {
return 'Ooops! Something went wrong :(';
}
};
console.log(foo('Hello')); // This logs `Promise { <pending> }`
I'm not sure what I'm doing wrong here as I'm not very familiar how promises work exactly, but how can I fix this?
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
return await axios.get(url).then(response => {
return new Promise((resolve, reject) => {
if (response.body.foobarArray.length === 0) {
return reject('No data found');
} else {
return resolve({
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
});
}
})
}).catch(err => {
return Promise.reject(err);
});
} catch (error) {
// return 'Ooops! Something went wrong :(';
return Promise.reject(`an error occurred : ${error}`);
}
};
foo('hello').then(result => {
console.log(result);
}).catch(err => {
console.log(`error ! : ${err}`);
});
async functions returns a promise. async functions use an implicit Promise to return its result. Even if you don't return a promise explicitly async function makes sure that your code is passed through a promise
as you are using axios asynchronous , it's response is a promise which must be handled inside .then().catch() functions .
if no error occurs you can access the response inside your .then() , else you will have access to your error on .catch()
inside your .then() you can now do what you want with data , returning a new Promise , using resolve() for success and reject() for failure .
You have 2 options here:
Option 1
Any async function returns a Promise (behind the scenes) so:
foo('Hello').then(console.log).error(console.error);
Option 2
You need to await for the result of foo function but, at the moment, you can't use await out of function scope level. So:
async function main() {
try {
const result = await foo('Hello');
console.log(result);
} catch (err) {
console.error(err);
}
}
main();
In future Node.js releases, using await at global scope will be allowed.
I wish to be able to return and end a function from inside a function inside that function. Just typing return inside the function will return for that function instead of the one I'm trying to return for.
function iWannaEscapeThis() {
function someSideFunction() {
//need to return iWannaEscapeThis here somehow
}
}
You need to call it main function and return it
function iwannaescapethis() {
function somesidefunction() {
return 'from inside'
}
return somesidefunction();
}
console.log(iwannaescapethis())
Async function
async function iwannaescapethis() {
async function somesidefunction() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("from inner"), 2000)
});
return await promise;
}
let x = await somesidefunction()
return x;
}
(async function(){
let res = await iwannaescapethis()
console.log(res)
})()
Return the nested function:
function iwannaescapethis() {
function somesidefunction() {
return "sideFunction";
}
return somesidefunction();
}
console.log(iwannaescapethis());
It's possible to use exceptions for doing this, but a big warning it's not advised you use exceptions for flow control too often.
But if you really do need this feature, here is an example.
class ReturnException extends Error {
constructor (value) { super(); this.value = value; }
};
function thisIsTheOuterFunction() {
try {
function someSubFunction() {
function someOtherSubFunction() {
throw new ReturnException("The answer is 42");
}
someOtherSubFunction();
}
someSubFunction();
} catch (e) {
if (e instanceof ReturnException) {
return e.value;
} else throw e;
}
}
console.log(thisIsTheOuterFunction());
I am creating a endpoint in node.js using ES6 Symbol. Example
// ES6 Symbol Method
const taskCreationMethod = {
[Symbol.taskMethod]() {
return {
storeCheckFunc: async function(storeId, employeeId) {
let store = await resourceModel["stores"].findById(storeId).populate(references["stores"]);
if(!store) {
return new Error("Store not found");
}
let employeeCheck = _.find(store.employees, (empObj) => {
return empObj._id == employeeId
})
if(!employeeCheck) {
return new Error("Employee not found");
}
return employeeCheck;
}
};
}
}
//end point
export const taskCreation = async(req, res) => {
const storeCheck = await taskCreationMethod[Symbol.taskMethod]().storeCheckFunc(req.body.store, req.body.assigned_to);
// here How can I handle return with Error Response?
}
You need to throw that error not just return it if you want to use the mechanisms of error handling. The thrown error will become a rejected promise which you can then handle with .catch() directly on the promise or with try/catch if you are using it in an async function. Here's a simplified example:
function populate() {
// always resolves to undefined
return Promise.resolve(undefined)
}
const taskCreationMethod = {
someMethod() {
return {
storeCheckFunc: async function() {
let store = await populate() // always resolves undefined
if (!store) { // so it always fails
throw new Error("Store not found"); // throw error
}
}
};
}
}
// regular promise then().catch()
taskCreationMethod.someMethod().storeCheckFunc()
.then(res => console.log(res))
.catch(err => console.log("Error:", err.message)) // catch
// OR … async function
async function runit() {
try {
let s = await taskCreationMethod.someMethod().storeCheckFunc()
} catch (err) {
console.log("Error:", err.message)
}
}
runit()
There is a service which gets the data and has a then-catch structure:
getData(siteId) {
const accessToken = JSON.parse(sessionStorage.getItem('ls.authorizationData')).token;
const config = { ...
};
this.canceler.resolve();
this.canceler = this.$q.defer();
config.timeout = this.canceler.promise;
const siteIds = this.MyService.getSitesIds();
return this.$http.get(`${TEST_API_URL}value?siteIds=${siteId || siteIds}`, config)
.then(result => {
return {
data: result.data,
};
}).catch((e) => {
if (e.status === -1 && e.xhrStatus === "abort") {
return Promise.resolve({canceled: true});
}
debugger;
this.hasData = false;
return Promise.reject('Cannot access data ');
});
}
I'm calling this function from the controller, initially in $onInit() like this:
$onInit() {
getData.call(null, this);
}
and as a function:
function getData(MyCtrl) {
MyCtrl.loading = true;
MyCtrl.MyService.getData(MyCtrl.siteId).then(result => {
if (result.canceled) {
return;
}
...
}
This works fine.
The problem appears when I want to send a variable from service to controller if data is not there (if the catch() happens).
I tried to change the return by wrapping the Promise inside an object like
return {promise: Promise.reject('Cannot access data ')};
and add the variable inside this object:
return {promise: Promise.reject('Cannot access data ')
hasData: false};
but it seems that it's not the right way. I need to know in the controller if the data was got or not.
Do you have any suggestions how to solve this?
Normally when you want to return more data from a Promise rejection, you do it the same way you would return from a normal exception, you extend from the Error object.
Here is an example..
class MyError extends Error {
constructor (message, extra) {
super(message);
this.extra = extra;
}
}
//standard promise rejection way.
function badPromise() {
return Promise.reject(
new MyError("Bad Error", "Extra Stuff")
);
}
//works if throw error inside an async function
async function badPromise2() {
throw new MyError("Bad Error2", "Extra Stuff2");
}
async function test() {
try {
if (Math.random() > 0.5) await badPromise();
else await badPromise2();
} catch(e) {
if (e instanceof MyError) {
console.error(`${e.message}, extra = ${e.extra}`);
} else {
console.error(`${e.toString()}`);
}
}
}
test();
ps. This is using new ESNext features, but the same applies if doing ES5..
I'm missing something on how to use async/await and probably promises methods too.
Here is what I am trying to do:
login-form-component.html
<button (click)="sinInWithFacebook()">Sign In with Facebook</button>
login-form-component.ts
async sinInWithFacebook() {
const loginResponse = await this.auth.signInWithFacebook();
console.log(loginResponse); // it returns undefinied
}
auth.service
signInWithFacebook() {
try {
this.fb.login(['email'])
.then((loginResponse: FacebookLoginResponse) => {
const credential = firebase.auth.FacebookAuthProvider.credential(loginResponse.authResponse.accessToken);
return <LoginResponse>{
result: this.auth.auth.signInWithCredential(credential)
}
})
}
catch (e) {
return {
error: e
}
}
}
loginResponse will always returns undefinied when I want it to return the result object. I believe it has something to do with asynchronous methods. Can you help me out?
Thanks
You should return the result from signInWithFacebook function:
try {
return this.fb.login(['email'])
.then((loginResponse: FacebookLoginResponse) => {
const credential = firebase.auth.FacebookAuthProvider.credential(loginResponse.authResponse.accessToken);
return <LoginResponse>{
result: this.auth.auth.signInWithCredential(credential)
}
})
}
your function doesn't return anything. And the try .. catch block doesn't work that way for Promises.
signInWithFacebook():Promise<LoginResponse> {
return this.fb.login(['email'])
.then((loginResponse: FacebookLoginResponse) => {
const credential = firebase.auth.FacebookAuthProvider.credential(loginResponse.authResponse.accessToken);
//my guts tell me that `this.auth.auth.signInWithCredential` is async as well.
//so let the promise chain deal with that/resolve that
return this.auth.auth.signInWithCredential(credential);
})
.then(
result => ({ result }),
error => ({ error })
);
}