lost promise in async await method - node.js

Hi guys, I'm newbie in node.js. I have function, that leads the dialogue with user in console. In newSwagger function I call the bumpDiff function, that shows changes between 2 files.
async function bumpDiff = () => {
exec('bump diff swagger.json newSwagger.json', (err, output) => {
// once the command has completed, the callback function is called
if (err) {
// log and return if we encounter an error
console.error("could not execute command: ", err)
return
}
// log the output received from the command
console.log("Output: \n", output)
})
};
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (str) => new Promise(resolve => rl.question(str, resolve));
const steps = {
start: async () => {
return steps.changeSwagger();
},
changeSwagger: async () => {
const addSwagger = await request();
console.log('success');
const changeSwagger = await question("Would you like to check changes in swagger? Please type yes/no: ");
if (changeSwagger === 'yes') { return steps.newSwagger(); }
if (changeSwagger === 'no') { return steps.oldSwagger(); }
return steps.end();
},
newSwagger: async () => {
console.log('showing changes');
const diff = await bumpDiff();
const ask = await question('Would you like to change swagger? Please type yes/no: ');
if (ask === 'yes') { return steps.changing(); }
if (ask === 'no') { return steps.end(); }
},
changing: async () => {
const rebuildSwagger = await SwaggerNew();
console.log('swagger changed successfully');
return steps.end();
},
oldSwagger: async () => {
console.log('No worries, have a nice day');
return steps.end();
},
end: async () => {
rl.close();
},
};
steps.start();
The problems is: when bumpDiff is starting, next readline
'Would you like to change swagger? Please type yes/no: '
come faster, then changes appeares. I guess I'm missing something about promises, can you help me find the mistake please.
P.S. all other functions, like 'request' and 'SwaggerNew' are async.

You are mixing two "styles" the callback approach in the "exec" function and you are trying to await that bumpDiff that is not returning a Promise (async/await approach), take a look to utils.Promisify helper function.
From NodeJS docs:
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);
async function lsExample() {
const { stdout, stderr } = await exec('ls');
console.log('stdout:', stdout);
console.error('stderr:', stderr);
}
lsExample();

You need to turn the callback style exec call into a promise so that await bumpDiff() can work properly.
async function bumpDiff = () => {
return new Promise((resolve, reject) => {
exec('bump diff swagger.json newSwagger.json', (err, output) => {
if (err) {
console.error("could not execute command: ", err)
reject(err)
} else {
console.log("Output: \n", output)
resolve(output)
}
})
})
};

Related

How to use promisify() with the spawn() function for the 'child_process'?

I have the following code example, and I have now ideas on how to resolve this using utils.promisify(); ONLY! Not Promise!
const spawn = child_process.spawn('docker', ['--version']);
spawn.stdout.on('data', (data) => {
process.stdout.write(data);
});
spawn.on('error', () => {
process.stderr.write(error);
process.exit(1);
});
The second code example works fine.
const promisifiedExecFile = promisify(child_process.execFile);
async function test() {
const version = await promisifiedExecFile('docker', ['--version']);
console.log(version);
}
test();
I couldn't quickly find out why the promisify function does now work properly with the spawn function. But you can create your own promisify function like this:
TS
import { spawn as spwn } from 'child_process';
const spawn = (
cmd: string,
args: ReadonlyArray<string>,
) => new Promise((resolve, reject) => {
const cp = spwn(cmd, args);
const error: string[] = [];
const stdout: string[] = [];
cp.stdout.on('data', (data) => {
stdout.push(data.toString());
});
cp.on('error', (e) => {
error.push(e.toString());
});
cp.on('close', () => {
if (error.length) reject(error.join(''));
else resolve(stdout.join(''));
});
});
(async () => {
try {
const stdOut = await spawn('docker', ['--version']);
console.log('stdOut: ', stdOut);
} catch (error) {
console.log('error:', error);
process.exit(1);
}
})();
JS
const { spawn: spwn } = require('child_process');
const spawn = (
cmd,
args,
) => new Promise((resolve, reject) => {
const cp = spwn(cmd, args);
const error = [];
const stdout = [];
cp.stdout.on('data', (data) => {
stdout.push(data.toString());
});
cp.on('error', (e) => {
error.push(e.toString());
});
cp.on('close', () => {
if (error.length) reject(error.join(''));
else resolve(stdout.join(''));
});
});
(async () => {
try {
const stdOut = await spawn('docker', ['--version']);
console.log('stdOut: ', stdOut);
} catch (error) {
console.log('error: ', error);
process.exit(1);
}
})();
Node.js' built-in util package has a promisify() function that converts callback-based functions to promise-based functions. This lets you use promise chaining and async/await with callback-based APIs.
I think that we can't use the promisify() with the spawn() function.
For example we can use promisify() with execFile() instead of spawn():
async asyncExecFile(tool) {
const execFile = util.promisify(child_process.execFile);
return await execFile(tool, ['--version'])
.catch(() => {
this.printError(`The "${tool}" don't exist in the current environment. \n`);
process.exit(0);
});
}
It is not possible because there is nothing to promisify. promisify works on functions where it takes a callback and spawn does not take a callback.
You use spawn by taking the returned ChildProcess then adding listeners to the ChildProcess' readable streams (stdout, stderr, stdio...)
Omar Omeiri's answer is similar to how execFile works inside node itself, so you can just use the promisified execFile instead. (if you need unlimited buffer, pass in maxBuffer: Infinity inside options)

Await Promise.all async does't work in my code

I have a lambda function with the structure below,
It used to work in older versions of nodejs but it doesn't work with the newer versions.
I know my code structure is quite messy and wrong but I can't get my head around it. I'm trying to use Promise.all but I'm obviously doing something wrong cause it's not getting executed at all.
By the way, I'm not getting any errors. The promise.all method never gets executed.
let AWS = require('aws-sdk');
exports.handler = async(event, context, callback) => {
let result = {};
try {
result = await getOrder(sql, 0);
result.map(
(dataField) => {
});
}
catch (error) {
console.log(error);
callback(error);
}
var today_result = [];
const groupKey = i => i.user_id + '_' + i.when;
const counts = _.countBy(followingsIDs, groupKey);
const isMulti = i => counts[groupKey(i)] > 1;
const multiPropkey = i => ({ multiplekey: isMulti(i) ? groupKey(i) : groupKey(i) });
const multiProp = i => ({ multiple: isMulti(i) ? counts[groupKey(i)] : 1 });
const updated = _.map(followingsIDs, i => _.extend(i, multiProp(i), multiPropkey(i)));
const uniqResult = _.uniq(updated, function(d) { return d.multiplekey });
// Doesn’t execute from here —>
await Promise.all(uniqResult.map(async(dataField) => {
console.log("test_");
dosomething()
if (true) {
let sql = `INSERT INTO ….`
result = await getOrder(sql, 0);
try {
const data = await sns.publish(params).promise();
}
catch (e) {
console.log(e.stack);
response.result = 'Error';
}
}
}));
// Till here <----
callback(null, uniqResult);
};
let getOrder = async(sql, params) => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, params, (err, results) => {
if (err) {
reject(err);
}
// console.log("-----Query Done!");
connection.release();
// console.log("-----Data: ", results);
resolve(results);
});
});
});
};
What are you awaiting to? The uniqResult is just declared as an empty array. Immediately after that you pass it to Promise.all. You need to fill it with Promises and then pass it to Promise.all.

In node.js, why is my data not getting passed back after a asynchronous file read using a Promise

I know for sure that my pullData module is getting the data back from the file read but the function calling it, though it has an await, is not getting the data.
This is the module (./initialise.js) that reads the data:
const fs = require('fs');
const getData = () => {
return new Promise((resolve, reject) => {
fs.readFile('./Sybernika.txt',
{ encoding: 'utf8', flag: 'r' },
function (err, data) {
if (err)
reject(err);
else
resolve(data);
});
});
};
module.exports = {getData};
And this is where it gets called (app.js):
const init = require('./initialise');
const pullData = async () => {
init.getData().then((data) => {
return data;
}).catch((err) => {
console.log(err);
});
};
const start = async() => {
let data = await pullData();
console.log(data);
}
start();
putting 'console.log(data)' just before return(data) in the resolve part of the call shows the data so I know it's being read OK. However, that final console.log shows my data variabkle as being undefined.
Any suggestions?
It's either
const pullData = async () => {
return init.getData().then((data) => {
return data;
}).catch((err) => {
console.log(err);
});
};
or
const pullData = async () =>
init.getData().then((data) => {
return data;
}).catch((err) => {
console.log(err);
});
Both versions make sure a promise returned by then/catch is passed down to the caller.

chrome extension - how i can await for chrome.runtime function?

My action in the background is to access the site and take information from there, the problem is that the code continues before the information is received.
Attached is a code that shows the problem:
background.js :
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
fetch(request.input, request.init).then(function(response) {
return response.text().then(function(text) {
sendResponse([{
body: text,
status: response.status,
statusText: response.statusText,
}, null]);
});
}, function(error) {
sendResponse([null, error]);
});
return true;
});
actions.js : after editing
const validateWord = async word => {
const input = "https://milog.co.il/" + word;
await new Promise(resolve => {
chrome.runtime.sendMessage({ input }, messageResponse => {
const [response, error] = messageResponse;
const parser = new DOMParser();
const html = parser.parseFromString(response.body, 'text/html');
const element = html.querySelector('.sr_e_txt');
console.log("aaa")
resolve(element?.innerText !== '');
});
});
};
validateWord("word")
.then(data => console.log(data))
.catch(reason => console.log(reason.message))
it prints aaa -> bbb -> word..
I want the "word" printed first and "aaa" wait for it to finish.
Thank you.
you can't use both a callback in a chrome method and await on the returned value because when a callback is used the method won't return a Promise.
bug in Chrome before 99: sendMessage doesn't return a Promise so you can't await it.
Fixed in Chrome 99.
So, in earlier versions of Chrome you can promisify the API:
promisifyApi(chrome.runtime, 'sendMessage');
(async () => {
const res = await chrome.runtime.sendMessage({ input });
console.log(res);
})();
function promisifyApi(thisArg, name) {
const fn = thisArg[name];
const localStack = new Error('Before calling ' + name);
thisArg[name] = (...args) => new Promise((resolve, reject) => {
fn(...args, result => {
let err = chrome.runtime.lastError;
if (err) {
err = new Error(err.message);
err.stack += '\n' + localStack.stack;
reject(err);
} else {
resolve(result);
}
});
});
}
...or use a callback:
chrome.runtime.sendMessage({ input }, res => {
// process `res` here
});

Make possible to get rejected value

I hope someone can help me out with my issue to get back the reject value out of this functions:
(async () => {
await queue.add(() => startCompare(orgpath, dpath, xray));
console.log('done compare ' + entry);
})();
This call the function:
async function startCompare(orgFile, compFile, xFile)
{
let gstderr;
return new Promise((resolve, reject) => {
spawn('compare', [orgFile, compFile, '-metric', 'AE', 'null:'])
.progress(function (childProcess) {
childProcess.stdout.on('data', function (data) {
console.log('[spawn] stdout: ', data.toString());
});
childProcess.stderr.on('data', function (data) {
gstderr = data.toString();
console.log('[spawn] stderr: ', data.toString());
});
}).then(res => {
resolve(true);
}).catch(error => {
resolve(gstderr);
});
});
}
My goal is how to get back gstderr value when rejected. Maybe the arrow function is the wrong way? I want to print out the value at: console.log('done compare ' + entry + xxxxx); where xxxxx is the rejected value.
You can catch the rejected value of a promise with async/await by using try/catch like so:
(async () => {
try {
// if it gets resolved lands here
const result = await startCompare(...args)
console.log(result) // should be true
} catch(error) {
// if it gets rejected it lands here
console.log(error) // should be the gstderr
}
})()
Hope it helps!
export const canLogin = createAsyncThunk("AUTH/login",
async (loginData ,{ rejectWithValue })=> {
try{
const response=await axios.post(AuthConfig.API_URL + AuthConfig.LOGIN_URI, loginData)
return response
}
catch(error){
if (!error.response || !error.message)
throw error
return rejectWithValue(getErrorMessage(error))
}
}
);
export function getErrorMessage(error) {
return (
error?.response?.data?.message ||
error?.response?.data.error ||
error?.response?.data ||
error?.message ||
error.toString()
);
}

Resources