i think both of them are giving me same promise but later one doesn't work as promise. it gives me "TypeError: data.then is not a function"
try{
await docClient.get(params).promise().then(x => console.log(x));//this one works
const data = await docClient.get(params).promise();
data.then(x =>console.log(x));//this.doesen't
}
catch(err){
console.log(err);
}
Function Logs:
START RequestId: 914e5709-7cf2-4978-9b58-d338ebee52dc Version: $LATEST
2019-11-08T04:39:22.259Z 914e5709-7cf2-4978-9b58-d338ebee52dc
INFO "Event: event"
2019-11-08T04:39:22.437Z 914e5709-7cf2-4978-9b58-d338ebee52dc
INFO { Item: { firstname: 'Bob', id: '12345', lastname: 'Johnson' } }
2019-11-08T04:39:22.487Z 914e5709-7cf2-4978-9b58-d338ebee52dc
INFO TypeError: data.then is not a function
at Runtime.exports.handler (/var/task/index2.js:21:11)
at process._tickCallback (internal/process/next_tick.js:68:7)
await docClient.get(params).promise().then(x => console.log(x));
Means that the entire expression docClient.get(params).promise().then(x => console.log(x)) returns a Promise and you await until it's resolved.
await docClient.get(params).promise()
Means you get a promise out of this part of the expression and await it thus unwrapping it into a plain value, which means that data is not a Promise any more and thus data.then(x =>console.log(x)) fails.
If you want to preserve the Promise in the second case, then you can not await it:
const data = docClient.get(params).promise();
await data.then(x =>console.log(x));
Although this is probably not very good, since you're better off using either await or Promises instead of breaking the chained Promise API to await part of it. You can just drop the Promises API altogether
const data = await docClient.get(params).promise();
console.log(data);
Worth noting that you can use use the alternative syntax with a callback but Promise or await are probably better:
const data = docClient.get(params, function(err, data) {
console.log(data);
});
In your first statement:
docClient.get(params).promise()
is a promise, so of course, you can add a .then() handler to it like:
docClient.get(params).promise().then(...)
Putting an await in front of it:
await docClient.get(params).promise().then(...)
doesn't affect the .then() itself, that's still a method call on docClient.get(params).promise(). It just awaits the result of the .then(). If you need some extra parens to see the order of evaluation, it would be like this:
await ( docClient.get(params).promise().then(...) )
though the extra parens are not needed in execution.
In your second statement:
const data = await docClient.get(params).promise();
Because of the await, the variable data contains the resolved value of the promise. It's not a promise. So, when you attempt:
data.then()
there's no .then() method on the value in the data variable so it's an error.
Like it says in the error, data.then is not a function. Since you awaited the promise the value is scalar at that point and you would need to console.log(data) to see it.
Related
I have created a function that downloads a PDF then reads it and finds the number of pages with chained promises. I want to return the value of the pages when this function is called.
However when I write var b = await ExtractLength(url, destinationFolder,filename); in my main function then console.log(b) I am getting a value of undefined. If I understand correctly the variable b is assigned the value before the promises are resolved. I have tried some alternatives but I don't understand what the problem is. Why is this happening and how I can fix it?
Here is my function:
async function ExtractLength(url, destination, flnm){
var options ={
directory: destination,
filename: flnm
}
await download(url, options, async function(error){
if (error){
console.log("Download error: ", error);
return null;
}
await fs.readFile(destination+flnm, async function(err,dataBuffer){
if (err){
console.log("Read file error: ", err);
return null;
}
await pdf(dataBuffer).then(function(data){
console.log("Number of pages: ", data.numpages);
return data.numpages;
})
.catch(function(erro){
console.log("Pdf error: ",erro);
return null;
});
});
});
}
Your code here is a bit of an antipattern. You usually shouldn't be using both a callback and a Promise. JS async and await are used to get the result of a promise into a variable. For example const x = await Promise.resolve(5); // x = 5
I believe AWS does support both callbacks and promises so just choose one or the other. It looks like you have a good handle on callbacks so the simplest way would probably be removing any async and await keywords. Promises are definitely worth learning!
It would be easier to give a definite solution using the two approaches if you showed us where the download function comes from.
Change the following lines
await download(url, options, async function(error){
await fs.readFile(destination+flnm, async function(err,dataBuffer){
await pdf(dataBuffer).then(function(data){
to have returns like so
return await download(url, options, async function(error){
return await fs.readFile(destination+flnm, async function(err,dataBuffer)
return await pdf(dataBuffer).then(function(data){
You must return a value for your Extract, your readfile function, and your pdf function calls.
To further explain: in each of those chained calls, you're passing anonymous functions for them call, and even though each anonymous function returns a value, you never assign the final value to anything within the Extract call that kicks it all off.
I need help with promises and then callbacks.
I have tried reading guides but don't understand them.
var lastMessage = msg.channel.fetchMessages({ limit: 2 }).then(messages => {
return messages.last();
})
This returns Promise { < pending > }.
.then() statements don't make the program wait for them to be completed, they just execute their code after the Promise they're attached to is resolved.
You can either decide to move the rest of the code inside that .then() statement (but it'll get really messy) or use async/await.
If you are inside a function, you can declare that as an async function: that allows you to use the await keyword inside it. await makes the program wait for a Promise to resolve, and instead of a Promise it returns the value that you would use in the .then() function.
Here's an example:
client.on('message', async () => {
// You can do everything you would normally do here
// Using the 'async' keyword allows you to later user 'await'
var lastMessage = await msg.channel.fetchMessages({ limit: 2 }).then(messages => {
return messages.last();
});
});
Partially adapted from this answer (also mine)
exports.handler = async (event, context, callback) => {
try {
const { headers, body } = event;
//This is where I forgot the "await" keyword
const input = ValidateInput(body); //Returns Promise
callback(null, true);
}catch(err){
console.log(err);
callback(null, false);
}
}
When calling a function that returns a promise and forgetting to create an await expression promise function call, and that function rejects the promise, Lambda logs this error in cloudwatch
(node:1) UnhandledPromiseRejectionWarning: #<Object>
The fix is simple, don't forget the await expression
const input = await ValidateInput(body); //Return Promise
The fix is simple, don't forget the await expression
const input = await ValidateInput(body); //Return Promise
As already mentioned, the solution is to ensure you await the promise:
const input = await ValidateInput(body);
But I thought I'd add a little context around why this occurs.
Since Promises can be stored in variables and chained at any point, there's no way for the library to know whether or not a Promise chain will have a .catch associated with it some time in the future. Many libraries therefore have a default behaviour of writing to the console if the rejected Promise has not been handled within a number of passes of the event loop; which is why you see this in the log.
You should generally take this warning as implying that you haven't awaited something you should have. As in reality, it's rare that you'd see it on purpose.
The AWS doc explicitly says you don't use callback with an async function.
The third argument, callback, is a function that you can call in
non-async functions to send a response. The callback function takes
two arguments: an Error and a response. The response object must be
compatible with JSON.stringify.
For async functions, you return a response, error, or promise to the
runtime instead of using callback.
So, you may want to fix that in your lambda function.
See here : https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback
I need help with debugging this code/or learn efficient way to do it- I tried using bluebird.each to capture all executions within my forEach, but didn't get it work. Same with setting up a new promise with pure javascript. I need help how to execute my forEach FIRST and move on.
let arr = [];
let fields = ['environment', 'habitat', 'earth]
Promise.each(fields, field => {
nano.db.use(field).view('options', 'options')
.spread((body, header) => {
arr.push(body.rows);
})
}).then(() => console.log(arr))
Expected outcome:
arr to console.log ====> [1,2,3,4,5]
Actual outcome:
arr is an empty array ====> [ ]
I see it's a problem with asynchronicity, but I can't seem to figure out how to actually make this work. any input or resources will be greatly appreciated!
I haven't actually ran your code so sorry if I'm incorrect, but from looking at it and the bluebird docs I assume the correction you need to make is return your nano.db call wrapped in a promise inside the Promise.each
let arr = [];
let fields = ['environment', 'habitat', 'earth']
Promise.each(fields, field => {
return new Promise ((resolve, reject) => {
nano.db.use(field).view('options', 'options')
.spread((body, header) => {
arr.push(body.rows);
resolve();
})
});
}).then(() => console.log(arr))
I believe your assumption is right that you're having a problem with asynchronicity when getting an empty array back instead of what you expect. I'm assuming the .then method is firing before the nano.db gets back with the data.
I wrapped your call of nano.db in a promise so that it will await nano.db finishing since Promise.each supports returning promises inside it.
Bluebird's promise docs state with Promise.each.
If the iterator function returns a promise or a thenable, then the
result of the promise is awaited before continuing with next
iteration.
So, if a promise is not returned in your Promise.each and anything that is asynchronous happens inside then just as with a then or a catch method on a promise under the same circumstances.
As I do not know bluebird there may be a way to change that promise to be more bluebird like. The promise I wrapped around the nano.db call is just a normal es6 promise which bluebird may or may not have a different api for creating promises.
I would like to have a function called on the return of the last promise made during a forEach() loop.
Sample code:
var mainList = getArrayData()
mainList.forEach((item,i)=>{
mongooseSchema.find(_id:item.id).exec((err,doc)=>{
doStuff(doc)
})
})
Any code after the second block will run immediately after all the mongo queries are sent off. If you wrap the whole thing in a promise (i.e. the array data is from a separate mongo query) you still get the same effect.
Is there any way I can have a special function/callback for the last query, or even better, a promise that returns after all the queries have returned?
Thanks
You could use Promise.all. It will fire off once all of the promises in an array are resolved.
The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
var mainList = getArrayData()
let promises = [];
mainList.forEach((item,i)=>{
let p = new Promise((resolve, reject) => {
mongooseSchema.find(_id:item.id).exec((err,doc)=>{
doStuff(doc)
resolve();
});
});
promises.push(p);
});
Promise.all(promises).then(() => {
// do something when all are resolved
});