I want to get the length of a song in youtube, but i cant make my program wait for my get_link function to end, even if i use async/await, my code will always return [Object, Promise]
async function get_link (song) {
search(song, opts, function(err, results) {
if(err) return console.log(err);
return new Promise(getlink => {
setTimeout(() => {
getlink(results[0].link);
}, 7000);
})
});
}
async function song_length (song) {
return ytdl.getInfo(song);
}
async function main () {
let link = await get_link("misery");
console.log("Link: " + link);
let len = await song_length(link);
console.log("Len: " + len);
}
I get an error in song_length because "link" is not an url, its a promise.
As I said in comments, an async function must always return a Promise. get_link() doesn't return anything (explicitely). It only performs search(), which is another async function.
Well actually, this is not quite right. An async function does return the last statement implicitely, if you don't specify any explicitely. So, it does return search(). But it does it immediately, while search() is only starting its execution, therefore search() is returned as a pending Promise that will never resolve.
async function get_link(song) {
return new Promise((resolve, reject) => {
search(song, opts, function(err, results) {
if (err) {
reject(err);
} else {
resolve(results)
}
});
})
}
Related
I have a module for nodejs where i search mx records of email service, then i call a reduce to find the one with lowest priority (i will connect to this mx record to send a message) and then i need to save this value (exactly smtp.gmail.com) and pass it into my another module. What do i do wrong ?
const dns = require('dns');
async function getMX() {
return new Promise(function (resolve, reject) {
dns.resolveMx('gmail.com', function (err, addresses) {
if (err) {
resolve(null);
}
else {
resolve(addresses);
}
});
}).then(listOfMX => { // here i'm looking for value with lowest priority
return arr = listOfMX.reduce((previous, current) => {
if (previous.priority < current.priority) {
return previous
}
return current;
})
}).then(response => {
console.log(response[0]);
return response.exchange; // i get {exchange:"smtp.aspko.com", priority: 5}, and from this i return only the exchange
}).catch(err => {
console.log(err);
});
};
async function f() { // async function to solve the Promise <pending>
const response = await getMX();
console.log(response)
};
f(); // here i call the main function
Anyway i still see or Promise or undefined and i can't just add a return in async function f(), which will solve my problem (when i will call f() it should return me the one value, but instead i see undefined)
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 am using a library call to connect to my vendor. The libary call requires a callback in the call. Without a callback in the function, I can easily make this synchronous. With the Callback, everything I do is stuck in the callback and never bubbles it way out.
I have literally tried 100 different ways to get this to work.
function removeFromDNC(emailAddress, accessToken_in)
{
return new Promise( function(resolve, reject)
{
try{
const options =
{
auth: {
accessToken: accessToken_in
}
, soapEndpoint: 'https://webservice.XXX.exacttarget.com/Service.asmx'
};
var co = {
"CustomerKey": "DNC",
"Keys":[
{"Key":{"Name":"Email Address","Value": emailAddress}}]
};
var uo = {
SaveOptions: [{"SaveOption":{PropertyName:"DataExtensionObject",SaveAction:"Delete"}}]
};
const soapClient = new FuelSoap(options);
//again, I don't control the structure of the next call.
let res = soapClient.delete('DataExtensionObject', co, uo, async function( err, response ) {
if ( err ) {
// I can get here, but my reject, or if I use return, does nothing
reject();
}else{
// I can get here, but my reject, or if I use return, does nothing
resolve();
}
});
console.log("res value " + res); // undefined - of course
}catch(err){
console.log("ALERT: Bad response back for removeFromDNC for email: " + emailAddress + " error: " + err);
console.log("removeFromDNC promise fulfilled in catch");
reject();
}
});
}
Both methods resolve and reject expect parameters, which are res and err in your case.
As far as removeFromDNC returns a Promise instance, you should call it using either async/await syntax:
const res = await removeFromDNC(...);
or chaining then/catch calls:
removeFromDNC(...)
.then((res) => { ... }) // resolve
.catch((err) => { ... }) // reject
EDIT:
If you want to avoid usage of callbacks inside removeFromDNC, consider promisifying of soapClient.delete call. Refer to util.promisify() if you working in Node.js or use own implementation.
Here is the example for demonstration:
const promisify = (fun) => (...args) => {
return new Promise((resolve, reject) => {
fun(...args, (err, result) => {
if(err) reject(err);
else resolve(result);
})
})
}
const soapClient = {
delete: (value, cb) => {
setTimeout(() => cb(null, value), 10);
}
};
async function removeFromDNC(emailAddress, accessToken_in) {
const soapDelete = promisify(soapClient.delete.bind(soapClient));
const res = await soapDelete('Soap Responce');
//You can use res here
return res;
}
removeFromDNC().then(res => console.log(res))
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 two functions that return promise. The first one provide host value, and the second one use the host value to get IP address. I can see that the first function is running without any issue. But looks like the callback function side getHostIps is not executed at all. Not sure why it happens....what's wrong with my promise function?
my promise chain:
getHostedZoneId(dns)
.then(hostZoneId => {
getHostIps(dns, hostZoneId);
})
.then(hostIps => {
logger.Info(hostIps); //hostIps is undefined
})
.catch(err => logger.error(err));
getHostedZoneId:
var getHostedZoneId = function(dns) {
var params = {
DNSName: dns,
};
return new Promise((resolve, reject) => {
findHostZoneByDNS(params, function(err, data) {
if(err) {
reject(err);
}
else {
resolve(data);
}
});
});
}
getHostIps:
var getHostIps = function(dns, hostZoneId) {
var params = {
HostedZoneId: hostZoneId,
StartRecordName: dns,
};
return new Promise((resolve, reject) => {
findHostIps(params, function(err, data) {
//logger.info("get there");
if(err) {
reject(err);
}
else {
resolve(data);
}
});
});
}
I logged hostIps and err and data, all of them are defined. So I am sure that the callback function inside promise is not executed. But not sure how to fix it.
Any feedback is appreciated! Thanks!
You have to return the promise from your then statement to complete the chain.
getHostedZoneId(dns)
.then(hostZoneId => {
return getHostIps(dns, hostZoneId); // Add return
})
.then(hostIps => {
logger.Info(hostIps);
})
.catch(err => logger.error(err));