using a returned object from asynchronous method - javascript

I am trying to do the following, unfortunately to no avail:
1. reading a spreadsheet and get it as a parsed object.
2. use the parsed object to look for a specific value in it. e.g an email.
The spreadsheet serves as a database and the motivation is to determine whether a given email exists there or not.
Apparently, I have misunderstood the use of asynchronous methods as I can't grasp how to return the.
The calling method:
helpers.read('the-spreadsheet-id', 'Sheet1!A:Z');
The reading method # helpers/sheets.js:
exports.read = (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(_.property('data.values'));
};
what should I do in order to get a json object or similarly parsed data object to keep on working with at the calling method?
Thanks in advance.

For the asynchronous step to work, you have to either declare you are returning a Promise or that the function is async. Async/await is actually using promises under the hood, but it's a cleaner syntax.
For the Promise version, you have to return the value you want with the resolve function, and throw the error with the reject function.
exports.read = (spreadsheetId, range) => {
return new Promise((resolve, reject) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(data => {
resolve(data.values);
})
.catch(err => reject(err));
});
};
When you are using async/await, you can wrap the query in a try/catch and throw the error if it fails.
exports.read = async (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
try {
const data = await sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
return data.values;
} catch(err) {
throw err;
}
};
I don't use underscore so I'm not sure what you were trying to return here. I assume that data is the result of the query and you were trying to pass out values?

Related

Using async/await with callback functions

I'm trying to build a service layer on top of mongo. I have a User object which has an array of referenced Achievements.
After I've authenticated the user by JWT or some other means, I enter the service layer and query the relationship as follows.
findForUser(userId: string | Types.ObjectId): Promise<Achievement[]> {
return new Promise((resolve) => {
UserSchema.findOne({ _id: userId },
async (err: any, user: User) => {
const AchievementMap: Achievement[] = [];
if (err) throw new Error(err);
user.achievements?.forEach((a) => {
// #ts-ignore
AchievementMap.push(a);
});
resolve(AchievementMap);
});
});
}
What is the async/await approach to returning the result of the callback method passed into UserSchema.findOne?
findOne returns an awaitable object - so you don't need to pass a callback to it. Don't try mixing callbacks with async/await. The only way to return the value from the callback, as a result of the constructed promise is by using resolve (only available in the promise executor).
Instead, make it all async - functionally similar but far cleaner.
async findForUser(userId: string | Types.ObjectId): Promise<Achievement[]> {
const user = await UserSchema.findOne({ _id: userId });
const AchievementMap: Achievement[] = [];
user.achievements?.forEach(a => {
AchievementMap.push(a);
});
return AchievementMap;
}
What is the async/await approach to returning the result of the callback
The async and await keywords are tools to manage promises. They aren't tools to manage callbacks.
The entire point of the code you have in the question is to wrap a promise based API around a function that deals in callbacks.
Now you have a promise you can await the return value of findForUser.
You can't use async and await instead of creating a promise.

async/await with Limiter for sending requests

I'm trying to limit the number of requests I send to an API.
I'm using Limiter and it's working just like I need, the only issue is that I can't find a way to use it with await (I need all the responses before rendering my page)
Can someone give me a hand with it?
Btw the Log returns a boolean.
const RateLimiter = require('limiter').RateLimiter;
const limiter = new RateLimiter(50, 5000)
for (let i = 0; i < arrayOfOrders.length; i++) {
const response = limiter.removeTokens(1, async (err, remainingRequests) => {
console.log('request')
return await CoreServices.load('updateOrder', {
"OrderNumber": arrayOfOrders[i],
"WorkFlowID": status
})
})
console.log('response', response)
}
console.log('needs to log after all the request');
this is loggin:
response true
response true
response false
needs to log after all the request
request
request
request
...
Promisifying .removeTokens will help, see if this code works
const RateLimiter = require('limiter').RateLimiter;
const limiter = new RateLimiter(50, 5000);
const tokenPromise = n => new Promise((resolve, reject) => {
limiter.removeTokens(n, (err, remainingRequests) => {
if (err) {
reject(err);
} else {
resolve(remainingRequests);
}
});
});
(async() => { // this line required only if this code is top level, otherwise use in an `async function`
const results = await Promise.all(arrayOfOrders.map(async (order) => {
await tokenPromise(1);
console.log('request');
return CoreServices.load('updateOrder', {
"OrderNumber": order,
"WorkFlowID": status
});
}));
console.log('needs to log after all the request');
})(); // this line required only if this code is top level, otherwise use in an `async function`
explanation
Firstly:
const tokenPromise = n => new Promise((resolve, reject) => {
limiter.removeTokens(n, (err, remainingRequests) => {
if (err) {
reject(err);
} else {
resolve(remainingRequests);
}
});
});
promisifies the limiter.removeTokens to use in async/await - in nodejs you could use the built in promisifier, however lately I've had too many instances where that fails - so a manual promisification (I'm making up a lot of words here!) works just as well
Now the code is easy - you can use arrayOfOrders.map rather than a for loop to create an array of promises that all run parallel as much as the rate limiting allows, (the rate limiting is done inside the callback)
await Promise.all(... will wait until all the CoreServices.load have completed (or one has failed - you could use await Promise.allSettled(... instead if you want)
The code in the map callback is tagged async so:
await tokenPromise(1);
will wait until the removeTokens callback is called - and then the request
return CoreServices.load
is made
Note, this was originally return await CoreServices.load but the await is redundant, as return await somepromise in an async function is just the same as return somepromise - so, adjust your code too

How to construct a Promise wrapper for MongoClient methods in Node.js

I'm trying to wrap various MongoDB methods in a Promise wrapper for a Node.js server but I'm getting a
TypeError: Promise is not a constructor
All the code inside the new Promise constructor wrapper works just fine on its own but I need it inside an asynchronous Promise in order to call it from another place.
const MongoClient = require("mongodb").MongoClient;
const Promise = require("promise");
const mongoURL = "...";
function checkURL (domain, path) {
return new Promise(function (resolve, reject) {
const client = new MongoClient(mongoURL, { useNewUrlParser: true });
client.connect()
.then(db => {
const collection = client.db("websites").collection("cool_websites");
return collection.find({domain: domain, path: path}).toArray();
})
.then(result => {
resolve(result);
client.close();
})
})
}
console.log(checkURL("www.stackoverflow.com", "/"));
I expect a console.log of
[ { _id: 123abc,
domain: 'www.stackoverflow.com',
path: '/questions/',
cool_website: true } ]
but in the terminal, I just recieve:
TypeError: Promise is not a constructor
You don't need to require("promise"), it doesn't make any sense. It's probably failing because of that.
Also, your test should be:
checkURL("www.stackoverflow.com", "/")
.then(result => {
console.log(result);
});
Because checkURL is a promise now.
#Titus answered my question. I was adapting a tutorial on Guru99 and they needed that package to use promises, but just deleting that line allowed everything to work. Thanks!

Retrieve Data from DynamoDB asynchronously using Promises

Following the documentation at
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
I am trying to write a basic test against my DynamoDB tables using promises to handle the asynchronous response with no luck thus far. It should return an object containing the TableNames.
If I access the DynamoDB object without using promises, I have no problem.
This works:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
return dynamo.listTables({Limit: 10}, (err, data) => {
if (err) {
console.log('err', err)
} else {
console.log('TableNames are:', data)
}
window.expect(data).toEqual(window.expect.anything())
})
})
This does not:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
let tables
const listPromise = dynamo.listTables({Limit: 10}).promise()
listPromise.then((data) => {
tables = data
console.log('TableNames are:', data)
}).catch((err) => {
console.log('Error:', err)
})
window.expect(tables).toEqual(window.expect.anything())
})
The value of data is always undefined. I cannot find any documentation that would indicate that promises are not supported with DynamoDB requests, so I am just about certain that I'm missing something obvious...
Any help is appreciated. This is my first time asking a question in this forum, so feedback on the question itself is also appreciated.
EDIT: Problem Solved - rookie mistake
The issue was not with dynamoDB, but rather with using test. When dealing with promises, the promise must be returned to test for proper evaluation. Otherwise, it cannot be evaluated.
Working Code:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
const listPromise = dynamo.listTables({Limit: 10}).promise()
return listPromise.then((data) => {
window.expect(data).toEqual(window.expect.anything())
}).catch((err) => {
console.log('Error:', err)
})
})
More info: https://facebook.github.io/jest/docs/en/asynchronous.html
Thanks to #Brahma Dev for the assistance.
Probem with test code, not Promise
The issue was not with dynamoDB or promises, but rather with my use of test. When dealing with promises, the promise must be returned to test for proper evaluation. Otherwise, it cannot be evaluated properly.
Working Code:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
const listPromise = dynamo.listTables({Limit: 10}).promise()
return listPromise.then((data) => {
window.expect(data).toEqual(window.expect.anything())
}).catch((err) => {
console.log('Error:', err)
})
})
More info: https://facebook.github.io/jest/docs/en/asynchronous.html
Thanks to #Brahma Dev for the assistance.
EDIT: Corrected working code example based on feedback from #Brahma Dev
Also, removed some unnecessary variable declarations and assignments.

How to update multiple Mongo documents manually in NodeJs?

I have a collection of users in MongoDB. I need to generate a token for each user and save it to the database:
var crypto = require('crypto');
db.users.find({}).exec(function(users){
users.forEach(function(user){
user.token = crypto.randomBytes(32).toString('hex');
user.save();
});
});
I'm always confused about async methods and just can't understand how they work... So this code doesn't work beacuse it exists before the save() calls finish. How would you make it work? How would you wait for all the save()˙calls and print Done! to console?
Thanks!
Mongoose find function returns a promise, you can use it to create chain. Promise.all produces promises (or a mix of promises and values), iterate over all the values and return a promise that is fulfilled when all the items in the array are fulfilled.
var crypto = require('crypto');
db.users
.find({})
.then(users => {
var ops = users.map(user => {
user.token = crypto.randomBytes(32).toString('hex');
return user.save();
});
return Promise.all(ops);
})
.then(() => console.log('done'))
.catch(err => console.log('error' + err));
});

Categories

Resources