Node.js async parallel not working how it should be? - javascript

I've got multiple promise' that I want to run one after the other, and I'm not sure I want to be returning the promises as it gets pretty messy!
So I decided to use the async library and implement the parallel method. Now I noticed that all my promises weren't running one, after the other, instead they were doing what promises are suppose todo (run + finish whenever).
I noticed that all the console.logs were running before all the promises were finished.
async.parallel([
(cb) => {
console.log ("hi")
grabMeData (Args)
.then ( (data) => {
// The promise is done and now I want to goto the next functio
cb();
}).catch(()=>console.log('err'));
},
(callback) => {
// The above promise is done, and I'm the callback
Query.checkUserExists()
.then ( () => {
if (Query.error) {
console.log (Query.error); // Determine error here
return; // Return to client if needed
}
callback();
});
},
() => {
// The above promise is done and I'm the callback!
// Originally wanted to be async
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id;
this.generateToken();
} else {
console.log ("account not created");
}
console.log ('xx')
}
], () =>{
console.log ("finished async parallel")
});
Any reason why my callbacks are being run before the promises are resolved (.then).

like Bergi said, async.js is redundant when you use promise, your code can be simplified as:
console.log('hi')
grabMeData(Args)
.catch(e => console.error(e))
.then(() => Query.checkUserExists())
.then(() => {
if (Query.accAlreadyCreated) {
this.NewUserModel.user_id = Query.user_id
return this.generateToken()
}
console.log ("account not created")
})
.then(() => console.log ('xx') )

Related

javascript Promise not wait Promise.all

So getAstronautsData make request to API then return array of promises. This promises mast make request to Wiki API and parse response in object. Then exampleAsyncFunc must wait all promises and return one big object with all info about Astronauts.
But if I use Promise.all function ending and console is clear.
function getAstronautsData() {
return new Promise((resolve, reject) => {
getData('http://api.open-notify.org/astros.json', "http", (data) => {
resolve(data) // get Astronauts list from API
})
}).then((astronautsList) => {
return astronautsList.people.map((person => // return array of promises
new Promise(resolve => {
getWikiData(person.name, (data) => { // request on Wiki API
resolve({info: data.extract, img: data.thumbnail.source})
})
})
))
})
}
async function exampleAsyncFunc (){
let promisesList = await getAstronautsData()
// next code just few variant was i try
let data = await Promise.all(promisesList)// it's not working.
console.log(data)
Promise.all(promisesList).then(data => console.log(data)) //it's not working. Function display nothing
promisesList.forEach((promise) => { //it's working but not so elegant
promise.then(data => console.log(data))
})
}
exampleAsyncFunc ()
function getWikiData(searhTerm, callback) {
getData(getUrlString(searhTerm), "https", (data) => {
const regex = new RegExp(searhTerm.replaceAll(" ", ".*"));
for (let page in data.query.pages) {
if (data.query.pages[page].title === searhTerm || regex.test(data.query.pages[page].title)) {
callback(data.query.pages[page])
return
}else{
callback(null)
}
}
})
}
You appear to be using Promise.all correctly, but if any of the Promises in Promise.all rejects, then overall Promise.all promise will reject too and nothing will happen, where in your forEach version it'll simply skip those promises silently and move on to the next entries.
Likewise if any of the promises in the list stays pending: if so then the Promise.all promise will never resolve. This could be because you have a long list of return values and the whole list takes a longer-than-expected time to resolve, or because your getWikiData call encounters an error and you don't pass that out to reject that particular promise in your array.
You can debug this behavior by ensuring that each of your calls to then is followed by .catch(console.error) (or some more robust error handler).
Let me first disclose that I am a big promise partisan and frankly deplore callbacks. The implication here is that I would not have written your getData and getWikiData with callbacks.
I will also point out that I second what #t.niese said in the comments: Because it does not make sense having both let data = await Promise.all(promisesList) and promisesList.forEach((promise) => {.
Anyway, your code is unnecessarily complex and can be simplified like so:
function getAstronautsData(callback) {
getData('http://api.open-notify.org/astros.json', "http", data => {
callback(data.people.map(person =>
new Promise(resolve => {
getWikiData(person.name, data => {
resolve(data);
})
}))
)
})
}
function exampleAsyncFunc (){
getAstronautsData(promises => {
Promise.all(promises)
.then(result => {
//result will contain those resolved promises
console.log(result);
})
});
}
exampleAsyncFunc ()
Notice that I am passing a callback to getAstronautsData and call it from inside that function with the array of promises you ultimately want to resolve. No need for async here either as you can see.
Ok, problem was in API (in API one of astronauts have name "Tom Marshburn" but on Wiki his page have title "Thomas Marshburn") and function getWikiData not return any data on error. So i fixed this problem.
Thanks you all for you help!!!

How to test then function of Promise.all with spyOn

I'm a newbie to coding, so please ask if more information is necessary.
I want to test a then-block inside a Promise.all with spyOn, but the function get never called.
public foo(): void {
const names = this.getNames();
Promise.all(
names.map(name =>
this.nameService.doSomething( //some params )
)
)
.then(result => this.controller.ok(names))
.catch(error => {
//do something
});
}
This is the test
it('should call controller.ok when name is set', () => {
spyOn(nameService, 'doSomething').and.returnValue(Promise.resolve());
spyOn(controller, 'ok');
service.foo();
expect(nameService.doSomething).toHaveBeenCalledWith({
//some params
});
expect(controller.ok).toHaveBeenCalled(); //fails because never called
});
I've debugged the code and the doSomething get called even with the right params, the code also reaches the then-block.
But the test says, it never gets called, so somewhere in there the code breaks and I don't know why?
The catch-block is not called.
Promises representing the eventual completion or failure of an asynchronous operation. Within your test, when checking if controller.ok has been called, the Promise returned by Promise.all of method foo was not resolved yet. Therefore you need some sort of synchronization.
One possible solution could look as follows.
it('should call controller.ok when name is set', () => {
const promises: Promise<any>[] = [];
spyOn(nameService, 'doSomething').and.callFake(n => {
const promise = Promise.resolve();
promises.push(promise);
return promise;
});
spyOn(controller, 'ok');
service.foo();
Promise.all(promises)
.then(r => expect(controller.ok).toHaveBeenCalled());
});
The same can be achieved by using fakeAsync and tick from #angular/core/testing.
it('should call controller.ok when name is set', fakeAsync(() => {
spyOn(nameService, 'doSomething').and.returnValue(Promise.resolve());
spyOn(controller, 'ok');
service.foo();
tick();
expect(controller.ok).toHaveBeenCalled();
}));

How can I make this JS code more functional using setTimeout and promises?

I'm trying to learn how to code using more functions and less loops and just in a more functional way. I want to implement a time out between calling connectBing. I was wondering if it's possible not to use the i variable and still get a 1 second time out between iterations. My code currently works but I'm looking for other ways to write it without using i.
This is my code:
// MAIN
getAllPosts().then((posts) => {
posts
.forEach( (post, i) => {
setTimeout(() => {
connectBing(anchorText,console.log).then()
} ,i * 1000)
})
// CONNECT TO BING WITH KW AND DO SOMETHING
function connectBing(anchorText,doSomethingWithBing) {
var deferred = q.defer();
request('https://www.cnn.com/search?q=' + anchorText, function (error, response, body) {
error ? console.log('error:', error) :
console.log('statusCode:', response && response.statusCode);
(doSomethingWithBing) ? doSomethingWithBing(body) : "You didn't give connectBing anything to do!"
})
return deferred.promise
}
You could take an array of async functions and chain each to run after the other. I will use native promises to demonstrate, and you can map it to the library you are using.
First create a function which takes in an array of async fucntions. It will chain one after the other, returning the last:
function chainAsyncFns(fns) {
// Ensure we have at least one promise to return
let promise = Promise.resolve();
fns.forEach(fn => promise = promise.then(fn));
return promise;
}
Then for each post, create an async function which will call connectBing and then wait for a timeout:
function connectBing() {
// Pretend we are connecting to a data source
return Promise.resolve();
}
function delay(ms) {
// Return a promise that resolves when the timeout is up
return new Promise(resolve => setTimeout(resolve, ms));
}
let fns = posts.map(post => () => {
return connectBing()
.then(() => delay(1000))
.catch(() => console.log('error'));
});
Chain the functions to run one after the other:
chainAsyncFns(fns).then(() => console.log('done'));

how to wait untill chain of asyc calls is succeeded

I have a function which is making an async call
and upon sucess, its making another async call
ex:-
function1 = () => {
/*Some required Logic*/
return fetch("myurl")
.then((json) => {
function2(json)
})
.catch((error) => {
console.log(error)
})
}
function2 = () => {
/*Some required Logic*/
return fetch("myurl2")
.then((json) => {
callthirdfunction(json)
})
.catch((error) => {
console.log(error)
})
}
now here is the function 3
in which i am dependent on the success of function1
function3 = () => {
/*Some required Logic*/
function1().then(() => {
})
}
Issue is it is only waiting untill the function1's asyc call is succeeded its not waiting for function2's async call to succeed
I know i can write like chain of asyn call but its not possible because of unavoidable circumstances
Any suggesstion or leads in this regard would be of great help
If you used async await as you mentioned in your tags, you could just use it like that:
await function1();
await function2();
await function3();
while adding the async keyword to the function declarations:
function1 = async () => {
}
Another option is to promisify your functions, and then call them like that:
function1()
.then(function2)
.then(function3)
.catch() {}
If you want one promise to depend on another promise, you need to return the dependent promise from then.
Your code doesn't do anything with the result of function2:
return fetch("myurl")
.then((json) => {
function2(json)
})
That should read
.then((json) => {
return function2(json)
})
The whole point of then is that it allows chaining of subsequent promises by returning them from the callback.
Promises get chained only if you return them. In the below example
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
Notice the return keyword inside resolveAfter2Seconds. Make sure that when you call a method that fires a promise, you add the 'return' keyword

Promises not going in order of the "then()"s? (nodejs)

Why does the then() where it says console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING'); statement come before the writing and extracting to PDFs? I'm new to Promises, so I am a bit confused by this. I want that then() statement to happen AFTER the writing and parsing happens.
function writeToPDF(data, fileName) {
return new Promise((resolve, reject) => {
data.pipe(fs.createWriteStream(fileName), err => {
reject();
});
data.on('end', () => {
resolve();
})
});
}
​
function extractPDF(pdf) {
return new Promise((resolve, reject) => {
extract(pdf, {splitPages: false}, (err, text) => {
if (err) {
console.log(err);
} else {
resolve(text);
}
});
});
}
​
request(link).then((success, failure) => {
let fileName = './name-' + Date.now() + '.pdf';
writeToPDF(data, fileName).then(() => {
extractPDF(fileName).then((text) => {
arrayOfDocuments.push(text);
})
}, () => {
//handle error
});
}).then(() => {
console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING');
});
You are nesting your then values and actually have two different then routes — for lack of a better term.
So far as the javascript is concerned, once the first then is resolved in your request method, it can move on to the next then. Here, once writeToPdf is resolved, that part of the promise chain then moves on to the console.log statement since the previous promise at that level has been resolved. That make sense?
The request(link).then(...) block comprises an outer promise chain and two levels of nested promise chain.
If you write a fat-arrow function with a {block}, returns are not explicit, therefore :
neither of the promises derived from writeToPDF() or extractPDF() is returned,
the outer promise chain is not informed of the existence of inner promises or their settlement,
console.log('THIS COMES BEFORE ...') relies solely on the fulfillment of the promise returned by request(link), and is guaranteed to occur after PDF stuff commences but before it completes.
request(link).then((success, failure) => {
let fileName = './name-' + Date.now() + '.pdf';
return writeToPDF(data, fileName).then(() => {
//^^^^^^
return extractPDF(fileName).then((text) => {
// ^^^^^^
arrayOfDocuments.push(text);
})
}, () => {
//handle error
});
}).then(() => {
console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING');
});

Categories

Resources