bluebird promise each return results - javascript

im using bluebird to chain few phantom related tasks in nodejs. My problem is that the last then in the chain gets the argument passed to the each as its results instead of the results of scrapDomain function
I've been messing with this for 2 days now, help please?
Promise chain:
Promise
.each(domains, function(domain) {
return ensureDir(domain[0])
})
.each(ssHome)
.each(function(domain){
return scrapDomain(domain).then(function(results){
return results
})
})
.then(function(results){
console.log(results)
})

See http://bluebirdjs.com/docs/api/promise.each.html
Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator function returns a promise or a thenable, then the result of the promise is awaited, before continuing with next iteration.
I think you want to use http://bluebirdjs.com/docs/api/promise.map.html

Use mapSeries which was designed exactly for this:
Promise
.mapSeries(domains, function(domain) {
return ensureDir(domain[0])
})
.mapSeries(ssHome)
.mapSeries(scrapDomain)
.mapSeries(console.log);
Although, personally I wouldn't write that code this way. First, each and mapSeries both execute sequentially, while other functions like map execute the function concurrently.
Second, in this use case each step has to wait for the previous step which is not necessary in your case. Instead, I'd write it like this:
const scrape = Promise.coroutine(d => { // Promise.coroutine is bluebird specific
let dir = ensureDir(d[0]);
let home = yield ssHome(dir);
let results = yield scrapDomain(home);
return results;
});
And then do:
let results = Promise.map(domains, scrape);
Which would let you do:
results.then(items => console.log(items));

Related

Async function not returning value, but console.log() does: how to do? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have an es6 class, with an init() method responsible for fetching data, transforming it, then update the class's property this.data with newly transformed data.
So far so good.
The class itself has another getPostById() method, to just do what it sounds like. Here is the code for the class:
class Posts {
constructor(url) {
this.ready = false
this.data = {}
this.url = url
}
async init() {
try {
let res = await fetch( this.url )
if (res.ok) {
let data = await res.json()
// Do bunch of transformation stuff here
this.data = data
this.ready = true
return data
}
}
catch (e) {
console.log(e)
}
}
getPostById(id){
return this.data.find( p => p.id === id )
}
}
Straightforward, except I have an async/await mechanism in the init() method.
Now, this code will work correctly:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')
allPosts.init()
.then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console
but it only gets printed into the console:
How could I use allPosts.getPostById(4) as a return of a function ?
Like:
let myFunc = async () => {
const postId = 4
await allPosts.init() // I need to wait for this to finish before returning
// This is logging correct value
console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )
// How can I return the RESULT of allPosts.getPostById( postId ) ???
return allPosts.getPostById( postId )
}
myFunc() returns a Promise but not the final value. I have read several related posts on the subject but they all give example of logging, never returning.
Here is a fiddle that includes two ways of handling init(): using Promise and using async/await. No matter what I try, I can't manage to USE the FINAL VALUE of getPostById(id).
The question of this post is: how can I create a function that will RETURN the VALUE of getPostById(id) ?
EDIT:
A lot of good answers trying to explain what Promises are in regards to the main execution loop.
After a lot of videos and other good reads, here is what I understand now:
my function init() correctly returns. However, within the main event loop: it returns a Promise, then it is my job to catch the result of this Promise from within a kinda parallel loop (not a new real thread). In order to catch the result from the parallel loop there are two ways:
use .then( value => doSomethingWithMy(value) )
use let value = await myAsyncFn(). Now here is the foolish hiccup:
await can only be used within an async function :p
thus itself returning a Promise, usable with await which should be embed in an async function, which will be usable with await etc...
This means we cannot really WAIT for a Promise: instead we should catch parallel loop indefinitely: using .then() or async/await.
Thanks for the help !
As for your comment; I'll add it as answer.
The code you write in JavaScript is run on one thread, that means that if your code could actually wait for something it will block any of your other code from getting executed. The event loop of JavaScript is explained very well in this video and if you like to read in this page.
A good example of blocking code in the browser is alert("cannot do anything until you click ok");. Alert blocks everything, the user can't even scroll or click on anything in the page and your code also blocks from executing.
Promise.resolve(22)
.then(x=>alert("blocking")||"Hello World")
.then(
x=>console.log(
"does not resolve untill you click ok on the alert:",
x
)
);
Run that in a console and you see what I mean by blocking.
This creates a problem when you want to do something that takes time. In other frameworks you'd use a thread or processes but there is no such thing in JavaScript (technically there is with web worker and fork in node but that's another story and usually far more complicated than using async api's).
So when you want to make a http request you can use fetch but fetch takes some time to finish and your function should not block (has to return something as fast as possible). This is why fetch returns a promise.
Note that fetch is implemented by browser/node and does run in another thread, only code you write runs in one thread so starting a lot of promises that only run code you write will not speed up anything but calling native async api's in parallel will.
Before promises async code used callbacks or would return an observable object (like XmlHttpRequest) but let's cover promises since you can convert the more traditional code to a promise anyway.
A promise is an object that has a then function (and a bunch of stuff that is sugar for then but does the same), this function takes 2 parameters.
Resolve handler: A function that will be called by the promise when the promise resolves (has no errors and is finished). The function will be passed one argument with the resolve value (for http requests this usually is the response).
Reject handler: A function that will be called by the promise when the promise rejects (has an error). This function will be passed one argument, this is usually the error or reason for rejection (can be a string, number or anything).
Converting callback to promise.
The traditional api's (especially nodejs api's) use callbacks:
traditionalApi(
arg
,function callback(err,value){
err ? handleFail(err) : processValue(value);
}
);
This makes it difficult for the programmer to catch errors or handle the return value in a linear way (from top to bottom). It gets even more impossible to try and do things parallel or throttled parallel with error handling (impossible to read).
You can convert traditional api's to promises with new Promise
const apiAsPromise = arg =>
new Promise(
(resolve,reject)=>
traditionalApi(
arg,
(err,val) => (err) ? reject(err) : resolve(val)
)
)
async await
This is what's called syntax sugar for promises. It makes promise consuming functions look more traditional and easier to read. That is if you like to write traditional code, I would argue that composing small functions is much easier to read. For example, can you guess what this does?:
const handleSearch = search =>
compose([
showLoading,
makeSearchRequest,
processRespose,
hideLoading
])(search)
.then(
undefined,//don't care about the resolve
compose([
showError,
hideLoading
])
);
Anayway; enough ranting. The important part is to understand that async await doesn't actually start another thread, async functions always return a promise and await doesn't actually block or wait. It's syntax sugar for someFn().then(result=>...,error=>...) and looks like:
async someMethod = () =>
//syntax sugar for:
//return someFn().then(result=>...,error=>...)
try{
const result = await someFn();
...
}catch(error){
...
}
}
The examples allways show try catch but you don't need to do that, for example:
var alwaysReject = async () => { throw "Always returns rejected promise"; };
alwaysReject()
.then(
x=>console.log("never happens, doesn't resolve")
,err=>console.warn("got rejected:",err)
);
Any error thrown or await returning a rejected promise will cause the async function to return a rejected promise (unless you try and catch it). Many times it is desirable to just let it fail and have the caller handle errors.
Catching errors could be needed when you want the promise to succeed with a special value for rejected promises so you can handle it later but the promise does not technically reject so will always resolve.
An example is Promise.all, this takes an array of promises and returns a new promise that resolves to an array of resolved values or reject when any one of them rejects. You may just want to get the results of all promises back and filter out the rejected ones:
const Fail = function(details){this.details=details;},
isFail = item => (item && item.constructor)===Fail;
Promise.all(
urls.map(//map array of urls to array of promises that don't reject
url =>
fetch(url)
.then(
undefined,//do not handle resolve yet
//when you handle the reject this ".then" will return
// a promise that RESOLVES to the value returned below (new Fail([url,err]))
err=>new Fail([url,err])
)
)
)
.then(
responses => {
console.log("failed requests:");
console.log(
responses.filter(//only Fail type
isFail
)
);
console.log("resolved requests:");
console.log(
responses.filter(//anything not Fail type
response=>!isFail(response)
)
);
}
);
Your question and the comments suggest you could use a little intuition nudge about the way the event loop works. It really is confusing at first, but after a while it becomes second nature.
Rather than thinking about the FINAL VALUE, think about the fact that you have a single thread and you can't stop it — so you want the FUTURE VALUE -- the value on the next or some future event loop. Everything you write that is not asynchronous is going to happen almost immediately — functions return with some value or undefined immediately. There's nothing you can do about. When you need something asynchronously, you need to setup a system that is ready to deal with the async values when they return sometime in the future. This is what events, callbacks, promises (and async/await) all try to help with. If some data is asynchronous, you simply can not use it in the same event loop.
So what do you do?
If you want a pattern where you create an instance, call init() and then some function that further process it, you simply need to setup a system that does the processing when the data arrives. There are a lot of ways to do this. Here's one way that's a variation on your class:
function someAsync() {
console.log("someAsync called")
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 1000)
})
}
class Posts {
constructor(url) {
this.ready = false
this.data = "uninitilized"
this.url = url
}
init() {
this.data = someAsync()
}
time100() {
// it's important to return the promise here
return this.data.then(d => d * 100)
}
}
let p = new Posts()
p.init()
processData(p)
// called twice to illustrate point
processData(p)
async function processData(posts) {
let p = await posts.time100()
console.log("randomin * 100:", p)
}
init() saves the promise returned from someAsync(). someAsync() could be anything that returns a promise. It saves the promise in an instance property. Now you can call then() or use async/await to get the value. It will either immediately return the value if the promise has already resolved or it will deal with it when it has resolved. I called processData(p) twice just to illustrate that it doesn't calle the someAsync() twice.
That's just one pattern. There are a lot more — using events, observables, just using then() directly, or even callbacks which are unfashionable, but still can be useful.
NOTE: Wherever you use await it has to be inside an async function.
Check out the UPDATED FIDDLE
You need to use await myFunc() to get the value you expect from getPostById because an async function always returns a promise.
This sometimes is very frustrating as the whole chain needs to be converted into async functions but that's the price you pay for converting it to a synchronous code, I guess. I am not sure if that can be avoided but am interested in hearing from people who have more experience on this.
Try out the below code in your console by copying over the functions and then accessing final and await final.
NOTE:
An async function CAN contain an await expression.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
There is no rule that is must have await in order to even declare an async function.
The example below uses an async function without await just to show that an async function always returns a promise.
const sample = async () => {
return 100;
}
// sample() WILL RETURN A PROMISE AND NOT 100
// await sample() WILL RETURN 100
const init = async (num) => {
return new Promise((resolve, reject) => {
resolve(num);
});
}
const myFunc = async (num) => {
const k = await init(num);
return k;
}
// const final = myFunc();
// final; This returns a promise
// await final; This returns the number you provided to myFunc

Loop through AWS Lambda Nodejs SDK function

I'm new to Nodejs and having trouble understand this issue: I tried to run a describe function against an array, and the AWS function seems to run after the main function has finished.
Here's the main function: (loop thru a list of ACM ARNs and check the status)
var checkCertStatus = function(resolveObj){
var promise = new Promise(function(resolve, reject){
console.log('1');
var retObj='';
resolveObj.Items.forEach(function(element) {
var certDescribeParams = {
CertificateArn: element.sslCertId
};
console.log('2');
acm.describeCertificate(certDescribeParams, function(err, data) {
if(err) reject(new Error(err));
else {
console.log(data.Certificate.DomainName + ': ' + data.Certificate.Status);
retObj+=data;
}
});
});
console.log('3');
resolve(retObj);
return promise;
})
}
Based on the debug log, assuming there are 2 items need to be processed, what I got:
1
2
2
3
example.com: ISSUED
example2.com: ISSUED
Basically, I need to pass this result to the next function in the chain (with promise and stuff).
Welcome to Node.js! Speaking generally, it might be helpful to study up on the asynchronous programming style. In particular, you seem to be mixing Promises and callbacks, which may make this example more confusing than it needs to be. I suggest using the AWS SDK's built-in feature to convert responses to Promises.
The first thing I notice is that you are manually constructing a Promise with a resolve/reject function. This is often a red flag unless you are creating a library. Most other libraries support Promises which you can simply use and chain. (This includes AWS SDK, as mentioned above.)
The second thing I notice is that your checkCertStatus function does not return anything. It creates a Promise but does not return it at the end. Your return promise; line is actually inside the callback function used to create the Promise.
Personally, when working with Promises, I prefer to use the Bluebird library. It provides more fully-featured Promises than native, including methods such as map. Conveniently, the AWS SDK can be configured to work with an alternative Promise constructor via AWS.config.setPromisesDependency() as documented here.
To simplify your logic, you might try something along these lines (untested code):
const Promise = require('bluebird');
AWS.config.setPromisesDependency(Promise);
const checkCertStatus = (resolveObj) => {
const items = resolveObj.Items;
console.log(`Mapping ${items.length} item(s)`);
return Promise.resolve(items)
.map((item) => {
const certDescribeParams = {
CertificateArn: item.sslCertId,
};
console.log(`Calling describeCertificate for ${item.sslCertId}`);
return acm.describeCertificate(certDescribeParams)
.promise()
.then((data) => {
console.log(`${data.Certificate.DomainName}: ${data.Certificate.Status}`);
return data;
});
});
};
We're defining checkCertStatus as a function which takes in resolveObj and returns a Promise chain starting from resolveObj.Items. (I apologize if you are not yet familiar with Arrow Functions.) The first and only step in this chain is to map the items array to a new array of Promises returned from the acm.describeCertificate method. If any one of these individual Promises fails, the top-level Promise chain will reject as well. Otherwise, the top-level Promise chain will resolve to an array of the results. (Note that I included an inessential .then step just to log the individual results, but you could remove that clause entirely.)
Hope this helps, and I apologize if I left any mistakes in the code.

Can you continue synchronously after a Promise.All?

Lets say I have the following code:
var myData = [];
var future = Promise.All([getPromise1, getPromise2]).then((result)=>{
var myData = result []
}).catch((err)=>{
\\Do something with err
})
doSomething (myData);
Can I do something with myData after it has received the data from promise.all result? Or do I have to handle subsequent code inside of a promises .then().
For Example:
future.then((data)=>{
doSomething(myData);
});
I feel like I would have to create a bunch of .then()'s in order to keep handling any type of synchronous code. Although I suppose since myData is the result then any code that uses the data in result should be handled as part of that promise like:
var future = Promise.All([getPromise1, getPromise2]).then((result)=>{
doSomething(result [])
}).catch((err)=>{
\\Do something with err
})
Am I just understanding this all wrong?
Looking forward to thoughts and opinions!
P.S im working on node.js
You're correct, the result of the promise can only be used within the promise's .then.
Side note - subsequent promises don't necessarily need to be nested, instead they can be chained. For example:
myPromise(foo).then(bar => {
// Do something with the result
baz = bar * 2
// Pass the result to some other promise
return mySecondPromise(baz)
}).then(foo2 => {
// Output the result of the second promise
console.log(foo2)
}).catch(err => {
console.error(err)
});
Errors will propagate down the chain.
You will have to handle subsequent code inside the .then() function callback. The reason you can't do anything outside of the promise chain is because of the way Promises work. a quick Google search about asynchronous javascript gives an explanation of why this is. The short answer is that doSomething(myData) will run before the code in the .then() function is run, which means that myData will still be an empty array when the script reaches that point in the code.
Actually even if the code did somehow reach the .then() function before the doSomething(myData) method, myData would still be an empty array because the myData declared in the .then() function is another variable entirely, since it is declared as
var myData
If you want to write code that looks more synchronous, I suggest using async/await, depending on if your version of node.js supports it or if you're using a transpiler. With async/await your code would look like:
async function() {
try {
var myData = await Promise.all([getPromise1, getPromise2]);
doSomething(myData);
} catch (ex) {
// Do something with err
}
}

Resolving promises synchronously with Q.all(promises)

I am using the Kriskowal Q library to handle promises.
I wrote the following function to wait until all promises have been resolved.
function multiplePromises(targetArray) {
var promises = {};
for (var i=0; i<targetArray.length; i++) {
var promise = singlePromise(); // any async. function
promises[i] = promise;
};
return Q.all(promises);
};
and I call it as follows:
multiplePromises(targetArray).then(
function(success){
console.log("success");
},
function(error){
console.log("error", error);
}
);
I was wondering however whether there is an order in which the promises are resolved (e.g. is it synchronous?). I.e. does the function wait to trigger the next promise i+1 until promise i is resolved? Or alternatively is it like with all other async. methods, that it actually fires all the single promises and just waits until they are all done?
If the second is the case, how would one rewrite this function to make sure that promise i+1 is ONLY triggered once promise i has been resolved?
Update: test
I did a test and put:
promises[i] = i;
to check whether it resolves sycnhronously and it seems the case. However, it could be just that my async function is fast enough to actually resolve it that quick. Does this seem right?
there are several ways to achieve what you want
Minimal change to your code would be
function multiplePromises(targetArray) {
var promises = [];
var p = Promise.resolve(); // I don't know the Q equivalent of this
for (var i=0; i<targetArray.length; i++) {
p = p.then(function() {
return singlePromise();
});
promises[i] = p;
};
return Q.all(promises);
};
The promises will be executed by Q in the order you declare them, but there's no way to ensure the return order will be the same. That is how async works.
If you want them to resolve in order, the only way I can think of is calling them one after the other instead of doing it in a batch.
This other response will provide more info and some solutions:
https://stackoverflow.com/a/36795097/7705625
Promises in theory could be resolved in order (and it could be easy to write an example in which they would) but you should never count on that.
The whole point of functions like Promise.all() (or Q.all() or Bluebird.all() etc.) is that if waits for all of the promises to get resolved, no matter what is the order of that, and as soon as they are all resolved then the promise that the Promise.all() itself returns gets resolved with an array of values that the original promises resolved to.
In that array you will get always the same order as the order of promises in the array that was an argument to Promise.all(). But the order in time in which the original promises were resolves is not known and is not relevant, as that will have no effect on the result of using Promise.all() whatsoever.

Q.all with chained sequence

I an using Q.js, and have a series of functions that return promises chained together with then callbacks, because, I need them to execute in order as a sequence.
var promiseOne = one();
promiseOne.then(two).then(three).done();
I also have some promise that can happen at any time. I need to know when they have ALL resolved - including the last then call back in the chain of promises. I was trying to use Q.all.then but it will reach Q.all's then before some of these promises -
Q.all([promiseOne, anotherPromise]).then(function(results) {
// will hit this before chain above is done
});
in my jsfiddle example, I just have the one promise in Q.all, but in reality it is one of many. Is there any way to make this work that that I can know when all, including chained then's, are resolved?
jsfiddle: http://jsfiddle.net/98rq0bs6/1/
A promise does not know what was chained to it. However, calling .then() does return a new promise which will know what it was chained from. You will need to take this new promise, which represents the result of the chained actions, and wait for it - instead of promiseOne, which was only the first link in the chain.
var promiseOne = one();
var promiseChain = promiseOne.then(two).then(three);
Q.all([promiseChain, anotherPromise]).then(function(results) {
// ^^^^^^^^^^^^ wait for the chain
…
}).done();
var listOfItems = [1, 2, 3, 4, 5];
// Creating an empty initial promise that always resolves itself.
var promise = $q.all([]);
// Iterating list of items.
angular.forEach(listOfItems, function (item) {
promise = promise.then(function () {
return $timeout(function () {
console.log('Item executed: ' + item);
}, 2000);
});
});
promise.finally(function () {
console.log('Chain finished!');
});
Source, In this solution, "angular.forEach" make possible that each promise be executed in chain, in other words, be a promises´s sequence

Categories

Resources