Nightmare then() invokes immediately after evaluate() - node.js

Consider the following code snippet:
nightmare
.evaluate(function (resolve){
setTimeout(function () {
resolve(null, 1234);
}, 1500)
})
.then(function (result) {
console.log('SUCCESS', result);
})
.catch(function (e) {
console.log('ERROR', e);
});
Assuming that nightmare instance has been initialized before, I have also used methods such as goto() and inject() (that part of code is very huge, but I can try cleaning it to present over there if necessary). But the result I get finally - is unlikely SUCCESS null instead of SUCCESS 1234.
I was experimenting with that and have understood that if call .wait(15000) before .evaluate() in that chain (or setup a breakpoint after .goto() and before the .evaluate() and wait some time when it's fired), the code works as it's exactly expected to.
What is wrong with that code?

Consider the following answer,
nightmare
.evaluate(function (){
return new Promise((resolve, reject)=>{
setTimeout(function () {
resolve(1234);
}, 1500)
})
})
.then(function (result) {
console.log('SUCCESS', result);
})
.catch(function (e) {
console.log('ERROR', e);
});
What does it do? It returns a promise. So the code will wait until it's resolved.
Also, you must resolve 1234 if you want to get 1234, and not a null.
Learn more about promises,
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://ponyfoo.com/articles/es6-promises-in-depth

Related

Catch multiple nested asynchronous function errors within a single catch block

The code below is an example of what may take place during development.
With the current code, the outer function may throw an error but in this case wont. However, the nested function WILL throw an error (for examples sake). Once it throws the error it cannot be caught as it is asynchronous function.
Bungie.Get('/Platform/Destiny2/Manifest/').then((ResponseText)=>{
//Async function that WILL throw an error
Bungie.Get('/Platform/Destiny2/Mnifest/').then((ResponseText)=>{
console.log('Success')
})
}).catch((error)=>{
//Catch all errors from either the main function or the nested function
doSomethingWithError(error)
});
What I want is for the outer most function to catch all asynchronous function error's but with this code I cannot. I have tried awaiting the nested function but there may be certain circumstances where it will be quicker to not wait for the function. I also tried to include a .catch() with each nested function but this would require a .catch() for each function that would allhandle the error in the same way e.g. doSomethingWithError().
you only needs return the inner function in the outside function.
see example below:
const foo = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo'), 1000);
});
foo.then((res)=>{
console.log(res)
return new Promise((resolve,reject)=>{
setTimeout(() => reject("bar fail"), 1000);
})
}).catch((e)=>{
// your own logic
console.error(e)
});
this is called promise chaining. see this post for more info https://javascript.info/promise-chaining
if you have multiple promises can do something like:
const foo1 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo1'), 1000);
});
const foo2 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo2'), 2000);
});
const foo3 = new Promise((resolve,reject) =>{
setTimeout(() => reject('foo3'), 3000);
});
const bar = new Promise((resolve,reject) =>{
setTimeout(() => resolve('bar'), 4000);
});
foo1
.then((res)=>{
console.log(res)
return foo2
})
.then((res)=>{
console.log(res)
return foo3 // throws the error
})
.then((res)=>{
console.log(res)
return bar
})
.catch((e)=>{
// every error will be cached here
console.error(e)
});
I would aim to use async / await unless you have very particular reasons, since it avoids callback hell and makes your code simpler and more bug free.
try {
const response1 = await Bungie.Get('/Platform/Destiny2/Manifest/');
const response2 = await Bungie.Get('/Platform/Destiny2/Mnifest/');
console.log('Success');
} catch (error) {
doSomethingWithError(error);
}
Imagine each Bungie call takes 250 milliseconds. While this is occurring, NodeJS will continue to execute other code via its event loop - eg requests from other clients. Awaiting is not the same as hanging the app.
Similarly, this type of code is used in many browser or mobile apps, and they remain responsive to the end user during I/O. I use the async await programming model in all languages these days (Javascript, Java, C#, Swift etc).
Try this:
let getMultiple = function(callback, ... keys){
let result = [];
let ctr = keys.length;
for(let i=0;i<ctr;i++)
result.push(0);
let ctr2 = 0;
keys.forEach(function(key){
let ctr3=ctr2++;
try{
Bungie.Get(key, function(data){
result[ctr3] = data;
ctr--;
if(ctr==0)
{
callback(result);
}
});
} catch(err) {
result[ctr3]=err.message;
ctr--;
if(ctr==0)
{
callback(result);
}
}
});
};
This should get all your data requests and replace relevant data with error message if it happens.
getMultiple(function(results){
console.log(results);
}, string1, string2, string3);
If the error causes by requesting same thing twice asynchronously, then you can add an asynchronous caching layer before this request.

Problem getting puppeteer-cluster waiting on page event before closing

I'm currently setting up a CI environment to automate e2e tests our team runs in a test harness. I am setting this up on Gitlab and currently using Puppeteer. I have an event that fires from our test harness that designates when the test is complete. Now I am trying to "pool" the execution so I don't use up all resources or run out of listeners. I decided to try out "puppeteer-cluster" for this task. I am close to having things working, however I can't seem to get it to wait for the event on page before closing the browser. Prior to using puppeteer-cluster, I was passing in a callback to my function and when the custom event was fired (injected via exposeFunction), I would go about calling it. That callback function is now being passed in data though now and therefore not waiting. I can't seem to find a way to get the execution to wait and was hoping someone might have an idea here. If anyone has any recommendations, I'd love to hear them.
test('Should launch the browser and run e2e tests', async (done) => {
try {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 10,
monitor: false,
timeout: 1200000,
puppeteerOptions: browserConfig
});
// Print errors to console
cluster.on("taskerror", (err, data) => {
console.log(`Error crawling ${data}: ${err.message}`);
});
//Setup our task to be run
await cluster.task( async ({page, data: {testUrl, isLastIndex, cb}, worker}) => {
console.log(`Test starting at url: ${testUrl} - isLastIndex: ${isLastIndex}`);
await page.goto(testUrl);
await page.waitForSelector('#testHarness');
await page.exposeFunction('onCustomEvent', async (e) => {
if (isLastIndex === true){ ;
//Make a call to our callback, finalizing tests are complete
cb();
}
console.log(`Completed test at url: ${testUrl}`);
});
await page.evaluate(() => {
document.addEventListener('TEST_COMPLETE', (e) => {
window.onCustomEvent('TEST_COMPLETE');
console.log("TEST COMPLETE");
});
});
});
//Perform the assignment of all of our xml tests to an array
let arrOfTests = await buildTestArray();
const arrOfTestsLen = arrOfTests.length;
for( let i=0; i < arrOfTestsLen; ++i){
//push our tests on task queue
await cluster.queue( {testUrl: arrOfTests[i], isLastIndex: (i === arrOfTestsLen - 1), cb: done });
};
await cluster.idle();
await cluster.close();
} catch (error) {
console.log('ERROR:',error);
done();
throw error;
}
});
So I got something working, but it really feels hacky to me and I'm not really sure it is the right approach. So should anyone have the proper way of doing this or a more recommended way, don't hesitate to respond. I am posting here shoudl anyone else deal with something similar. I was able to get this working with a bool and setInterval. I have pasted working result below.
await cluster.task( async ({page, data: {testUrl, isLastIndex, cb}, worker}) => {
let complete = false;
console.log(`Test starting at url: ${testUrl} - isLastIndex: ${isLastIndex}`);
await page.goto(testUrl)
await page.waitForSelector('#testHarness');
await page.focus('#testHarness');
await page.exposeFunction('onCustomEvent', async (e) => {
console.log("Custom event fired");
if (isLastIndex === true){ ;
//Make a call to our callback, finalizing tests are complete
cb();
complete = true;
//console.log(`VAL IS ${complete}`);
}
console.log(`Completed test at url: ${testUrl}`);
});
//This will run on the actual page itself. So setup an event listener for
//the TEST_COMPLETE event sent from the test harness itself
await page.evaluate(() => {
document.addEventListener('TEST_COMPLETE', (e) => {
window.onCustomEvent('TEST_COMPLETE');
});
});
await new Promise(resolve => {
try {
let timerId = setInterval(()=>{
if (complete === true){
resolve();
clearInterval(timerId);
}
}, 1000);
} catch (e) {
console.log('ERROR ', e);
}
});
});

Wait for nodeJs async/promise process to end before sending response to client

I have the following code that I cannot figure out what I need to do to make this entire process finish before sending a response back to the client. Basically I am looking for the client to receive a response once everything in the method completes or errors out, not before.
I have tried several things, moving things around and adding some .then() and .catch() blocks that may not even be needed. I'm still fairly new to NodeJs so while I understand how things kind of work, I still have not got used to asynchronous coding.
let markStepsComplete = async function() {
let stepsToProcess = new Multimap();//multimap JS npm
let results = await dbUtils.getCompletedSteps();
results.forEach(result => {
stepsToProcess.set(result.ticket_id, result.step_id);
});
return new Promise((resolve,reject)=>{
stepsToProcess.forEachEntry(async function(entry, key) {
let payload = {
ticketId: key,
stepIds: entry
}
let response = await updateStep(payload)//returns promise
if (response.statusCode === 200) {
await Promise.all(payload.stepIds.map(async (stepId) => {
try {
await dbUtils.markStepsCompleted(stepId, payload.ticketId);
} catch(err) {
reject(err);
}
}));
}
else {
await Promise.all(payload.stepIds.map(async (stepId) => {
try {
await dbUtils.markStepsProcessed(stepId, payload.ticketId, response.statusCode);
} catch(err) {
console.log(err);
reject(err);
}
}));
}
});
resolve('Steps Marked Complete');
});//promise end
}
This is the path that i am testing right now that's making the call to the above method.
The updateStep() method is the method that actually calls a HTTP request to an outside REST API. That seems to be working returning a promise.
app.use('/api/posts',async (req,res,next)=>{
let data = await markStepsComplete.markStepsComplete()
.then((data)=>{
res.status(200).json({message: data})
})
.catch((e)=>{
console.log('error from catch:',e);
})
})
The code above runs and does run some stuff in our database as part of this process, but I get back the "Steps Marked Complete" resolve message before the process finishes.
Since the resolve is hit before the process ends, I can never get any of the reject() errors to come back to the client since resolve is called already.
Thanks in advance.
I noticed this snippet and wanted to call it out because it wont work:
payload.stepIds.forEach(async (stepId) => {
try {
await dbUtils.markStepsProcessed(stepId, payload.ticketId, response.statusCode);
} catch(err) {
console.log(err);
reject(err);
}
});
when looping through an array of items that require an async fetch, you can do something like:
await Promise.all(payload.stepIds.map(async (stepId) => {
try {
await dbUtils.markStepsProcessed(stepId, payload.ticketId, response.statusCode);
} catch(err) {
console.log(err);
reject(err);
}
}));

NodeJS fs.appendFile not working within promise in mocha

I want to keep a log during my integration test suite. I'm testing that every 'item' is being compiled and logging how much time it took. I'm using node 4.3.
First of all I create the log file:
before(function() {
if (!fs.existsSync("./log.csv")) {
fs.writeFile("./log.csv", "Name; Time");
}
});
Then within each it block I would do this:
for (const item of items) {
it('compiles', function() {
return item.testCompile();
});
}
And item class has these methods:
testCompile() {
return this
.buildItem()
.then(result => {
// whatever testing stuff
});
}
buildItem() {
return this
.internalLogicForCompiling()
.then(result => {
// This is not appending anything
fs.appendFile("./log.csv", `${item.name}; ${result.compileTime}`);
return result;
});
}
So far the file is created but never updated... Any clue what I'm doing wrong?
PS: I assume if the file doesn't exists, fs should throw an error, however it doesn't.
Your code is generally ignoring the fact that your fs calls are asynchronous. Promises are not magic. If you use code that is asynchronous but does not use promises, you need to do more than plop that code in a promise can call it done.
The easiest way to deal with the issue would be to use fs.writeFileSync and fs.appendFileSync instead of the calls you make. Otherwise, you should write your before like this:
before(function(done) {
if (!fs.existsSync("./log.csv")) {
fs.writeFile("./log.csv", "Name; Time", done);
}
});
I've just added the done callback.
And buildItem could be something like this so that the promise it returns won't resolve before appendFile is done doing its work:
buildItem() {
return this
.internalLogicForCompiling()
.then(result => {
return new Promise((resolve, reject) => {
fs.appendFile("./log.csv", `${item.name}; ${result.compileTime}`, (err) => {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
});
}

Creating functions to chain promises correctly

I am trying to get Promise chaining working for me correctly.
I believe the problem boils down to understanding the difference between:
promise.then(foo).then(bar);
and:
promise.then(foo.then(bar));
In this situation I am writing both foo and bar and am trying to get the signatures right. bar does take a return value that is produced by foo.
I have the latter working, but my question is what do I need to do to get the former working?
Related to the above is the full code (below). I don't have the different logs printed in the order I am expecting (expecting log1, log2, log3, log4, log5, but getting log3, log4, log5, log1, log2). I am hoping as I figure the above I will get this working right as well.
var Promise = require('bluebird');
function listPages(queryUrl) {
var promise = Promise.resolve();
promise = promise
.then(parseFeed(queryUrl)
.then(function (items) {
items.forEach(function (item) {
promise = promise.then(processItem(transform(item)))
.then(function() { console.log('log1');})
.then(function() { console.log('log2');});
});
}).then(function() {console.log('log3')})
).then(function() {console.log('log4')})
.catch(function (error) {
console.log('error: ', error, error.stack);
});
return promise.then(function() {console.log('log5');});
};
What is the difference between promise.then(foo).then(bar); and promise.then(foo.then(bar));?
The second one is simply wrong. The then method takes a callback as its argument, not a promise. That callback might return a promise, so the first one is equivalent to
promise.then(function(x) { return foo(x).then(bar) })
(assuming that foo returns a promise as well).
Your whole code appears to be messed up a bit. It should probably read
function listPages(queryUrl) {
return parseFeed(queryUrl)
.then(function (items) {
var promise = Promise.resolve();
items.forEach(function (item) {
promise = promise.then(function() {
console.log('log1');
return processItem(transform(item));
}).then(function() {
console.log('log2');
});
});
return promise;
}).then(function() {
console.log('log3')
}, function (error) {
console.log('error: ', error, error.stack);
});
}

Resources