I have a function that can return result in both callback and promise:
function foo(args, cb) {
// do stuff
const promise = getSomePromise();
if (cb) {
promise.then((result) => {
cb(null, result);
}, (err) => {
cb(err);
});
} else {
return promise;
}
}
I want to alter the result of promise before returning it. How to do this in a way that introduces the least amount of spaghetti code?
Add a function to alter and return modified result
function alterResult(result){
const alteredResult = /// do something to result
return alteredResult;
}
Then add it in a then()to:
const promise = getSomePromise().then(alterResult);
You can write a function which returns something and pass it to the callback when initiating it like this
function foo(cb) {
const promise = getSomePromise(); // return some promise
promise.then((result) => { // result is here just "Hello"
cb(doSomething(result)); // return the altered value here to the callback
})
}
function getSomePromise() {
return new Promise((resolve, _) => resolve("Hello")) // make a new promise
}
function doSomething(res) { // define a function that alters your result
return res + ", World"
}
foo((val) => console.log(val)) // log it
Related
Here is a function to find mx records of a service and i need to save the one value(with the lowest priority) to make a request to it. How can I save and return this value?
const dns = require('dns');
const email = '...#gmail.com'
let res = email.split('#').pop();
function getMxRecords(domain) {
return new Promise(function(resolve, reject) {
dns.resolveMx(domain, function(err, addresses) {
if (err) {
//console.log(err, err.stack)
resolve(null);
} else {
//console.log(addresses);
let copy = [...addresses];
//console.log(copy);
let theone = copy.reduce((previous, current) => {
if (previous.priority < current.priority) {
return current;
}
return previous;
});
resolve(theone);
}
});
});
}
let a = getMxRecords(res);
console.log(a);
Yeah, so i need to export this module to make a request to it like below;
let socket = net.createConnection(25, request(email), () => {})
so for this my function should request me or array or object with only one value, when i'm trying it doesn't work, i always get this:
Promise { } //HERE IS RETURN FROM MY FUNCTION (WITH .THEN)
Error in socket connect ECONNREFUSED 127.0.0.1:25
A Promise is mostly an asynchronous call. It returns an Promise-Object that will resolve or reject the Promise. To access the result, you will call some functions:
function getMxRecords(domain) {
return new Promise(function(resolve, reject) {
dns.resolveMx(domain, function(err, addresses) {
if (err) {
//console.log(err, err.stack)
resolve(null);
} else {
//console.log(addresses);
let copy = [...addresses];
//console.log(copy);
let theone = copy.reduce((previous, current) => {
if (previous.priority < current.priority) {
return current;
}
return previous;
});
resolve(theone);
}
});
});
}
getMxRecords(res)
.then(yourResolveValueProvided => {
// Your code if the promise succeeded
})
.catch(error => {
// Your code if the promises reject() were called. error would be the provided parameter.
})
In the code below , Values are RETURNED correctly from a queued Promise.then() chain .
CODE:
let cond_1 = true;
let data = 'Data Received....';
let err = 'Error';
var p1 = new Promise(function(resolve,reject){
if(cond_1){
resolve(data);
}else{
reject(err); }})
p1.then((data)=>{console.log(data);return 'Wait....';})
.then((val1)=>{console.log(val1); return 'Finished';})
.then((val2)=>{console.log(val2)})
.catch((err)=>{console.log(err)});
Output :
Data Received....
Wait....
Finished
However, the same RETURNED values from a chained SetTimeout function are returned 'UNDEFINED'.
CODE:
p1.then((data)=>{console.log(data); return 'Wait.....'; })
.then((val1)=>{setTimeout(function(val1){console.log(val1); return 'Finished';},1000)})
.then((val2)=>{setTimeout(function(val2){console.log(val2);},1000)})
.catch((err)=>{console.log(err)});
Output:
Data Received....
undefined
undefined
How to resolve this?
Try taking advantage of Lexicographic nature of Javascript.
Instead of making a function v1,v2 which your functions takes within setTimeout, just use an arrow function. In this way you are using the v1,v2 returned from promise.
Do this
let cond_1 = true;
let data = 'Data Received....';
let err = 'Error';
var p1 = new Promise(function(resolve, reject) {
if (cond_1) {
resolve(data);
} else {
reject(err);
}
})
p1.then((data) => {
console.log(data);
return 'Wait.....';
})
.then((val1) => {
setTimeout(() => {
console.log(val1);
}, 1000);
return 'Finished';
})
.then((val2) => {
return setTimeout(() => {
console.log(val2)
}, 1000)
})
.catch((err) => {
console.log(err)
});
What you did was you created a new variable v1,v2 for your function. You can only use that when you pass value v1,v2 in that function. That function won't use v1,v2 returned from promise as you expect.
When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});
When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});
I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:
let promise = new Promise((resolve, reject) => {
// Do some stuff
});
doSomethingUntilPromiseisDone(promise);
However, within my Promise, I need to execute another method that returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
// Do something here
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
saveToCache(value)
.then((success) => {
console.log('success!!');
resolve('success');
});
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.
Use .then()
let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
promise
.then(value => fetchValue(url))
.then(value => value.blob())
.then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))
you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)
//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
try {
const value = yield fetchValue(url);
const success = yield saveToCache(value);
console.log('success:', success);
} catch(e) {
console.error(err);
}
}));
generator();
Each function will call the next one with the result of the method before.
var promises = [1,2,3].map((guid)=>{
return (param)=> {
console.log("param", param);
var id = guid;
return new Promise(resolve => {
// resolve in a random amount of time
setTimeout(function () {
resolve(id);
}, (Math.random() * 1.5 | 0) * 1000);
});
}
}).reduce(function (acc, curr, index) {
return acc.then(function (res) {
return curr(res[index-1]).then(function (result) {
console.log("result", result);
res.push(result);
return res;
});
});
}, Promise.resolve([]));
promises.then(console.log);