I have a similar problem like Use Async Functions in DialogFlow WebHook but that solution, changing request-promises for request-promises-native, didn't work for me, the difference it's that I'm using actions-on-google lib with ActionsSDK instead DialogFlow one, here is my code:
function call() {
var options = {
url: "https://google.es"
};
return request(options)
.then((res) => {
console.log("Success", res);
Promise.resolve();
})
.catch((err) => {
console.log("Error", err);
Promise.resolve();
});
}
const handleAction = (conv) => {
call()
.then(() => {
console.log("Going to ASK");
conv.ask('Hi, how is it going?');
return Promise.resolve();
})
.catch(error => {
console.log("Ask ERROR");
conv.ask('Hi, how is it going?');
return Promise.resolve();
});
}
app.intent('actions.intent.MAIN', (conv) => {
handleAction(conv);
});
If I change the call function for this one:
function call() {
let prom = new Promise((resolve,reject) =>{
resolve();
});
return prom;
}
It works like a charm. I don't understand where I'm wrong, I'm returning promises until the intent. Any idea?
Thanks!
you can resolve this problem using async/await. It will look like this. It might help you.
(async () => {
async function call() {
var options = {
url: "https://google.es"
};
return await new Promise((resolve, reject) => {
request(options)
.then((res) => {
resolve(res);
})
.catch((err) => {
console.log("Error", err);
reject(err)
});
}
}
const handleAction = await call(); //you will get result on handle Action varaible
})();
Related
How can I use the answer of a promise outside of. Then what should I do?
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
console.log(resp)
})
.catch(e => console.log(e))
}
});
console.log (resp) inside the .then () knows it and prints correctly, but when I want to know resp out of the forEach to use below, it says undefined
Thanks.
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
logMyData(resp);
})
.catch(e => console.log(e))
}
});
logMyData=(x)=>{
console.log(x);
}
This is just as simple as adding a helper function which executes inside your .then
Guessing that you want to be able to access the value within the forloop. Since self.campoSelects is a promise we can use async await.
// Call campo selects
function getCampoSelects(_self, tablaCampo) {
return new Promise(async (resolve, reject) => {
let campoData;
try {
campoData = await _self.campoSelects(tablaCampo);
} catch (err) {
reject(err);
}
resolve(campoData);
});
}
function happyLittleFunc() {
const arreglo = [];
arreglo.forEach(async (item) => {
if (item.tipoCampo === 3) {
let campoSelect;
// Unsure if you are setting self somewhere but it can be passed in here.
try {
campoSelect = await getCampoSelects(self, item.tipoCampo);
} catch (err) {
console.log(err);
return;
}
console.log(campoSelect);
}
});
}
happyLittleFunc();
How to call "n" number of API calls in the sequentially order(each API response as input parameter for next API call) in the Node.js?
Please, review below example of promise in which you may find that it will execute synchronously as your requirement.
let firstPromise = (() => {
return new Promise((resolve) => {
resolve("first promise");
});
});
let secondPromise = (() => {
return new Promise((resolve) => {
resolve("second promise");
});
});
let thirdPromise = (() => {
return new Promise((resolve) => {
resolve("third promise");
});
});
let fourthPromise = (() => {
return new Promise((resolve) => {
resolve("fourth promise");
});
});
firstPromise()
.then((res) => {
console.log(res);
return secondPromise();
})
.then((res) => {
console.log(res);
return thirdPromise();
})
.then((res) => {
console.log(res);
return fourthPromise();
})
.then((res) => {
console.log(res);
})
.catch((err)=>{
throw new Error(err);
});
I am trying to download tracks via the soundcloud API, and then launch a callback once an indeterminant amount of tracks is downloaded. When I run the below code, I see "All done" being console logged before anything else, even though I intend for it to be the last thing... What am I doing wrong?
// Deps
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
// Write mp3 function
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
async function asyncTrackFetch(track) {
return await writeMP3(track)
}
// Array of promises to callback upon
const trackActions = []
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
}
})
// Perform requests async
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
Promise.all(trackActions) waits on whatever promises are in trackActions, but trackActions is empty at the time you make the call. You're only adding promises to the array after your SC.get callback gets called.
Try putting your Promise.all... block inside the SC.get callback like this:
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
}
})
It's worth mentioning as well that your line throw new Error(err) will crash the program since there's nowhere for that error to be caught.
As Antonio Val mentioned, there are better ways to do this. If you promisify the node-soundcloud library then the last part of your code could look like this:
SC.get('/tracks').then(tracks => {
// No need for trackedActions array.
return Promise.all(tracks.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track)))
}).then(fetchedTracks => {
console.log('All done fetching tracks', fetchedTracks)
}).catch(err => {
// Handle error.
})
Or inside an async function,
try {
const tracks = await SC.get('/tracks')
const fetchPromises = tracks
.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track))
const fetchedTracks = await Promise.all(fetchPromises)
console('All done fetching tracks.', fetchedTracks)
} catch (err) {
// Handle error
}
I think the easiest way would be to move Promise.all after tracks.map loop finished.
A more elegant solution would be to promisify SC.get as well and use async await along all your code.
UPDATE:
Couldn't test it so not sure if it works, but it would be something like this:
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
function getTracks() {
return new Promise((resolve, reject) => {
SC.get('/tracks', (err, tracks) => {
if (err) {
return reject(err)
}
console.log('Tracks fetched: ', tracks.length)
resolve(tracks)
})
})
}
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
With async await:
async function start() {
const tracks = await getTracks();
for (let track of tracks) {
await writeMP3(track)
}
}
start()
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
If you just want to use Promises:
getTracks
.then((tracks) => {
const promiseArray = tracks.map((track) => {
return writeMP3(track)
})
return Promise.all(promiseArray)
})
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
I am getting an error that seems to suggest I'm not returning some of my statements, but I think I'm doing everything correctly. Here's the warning:
Warning: a promise was created in a handler at /src/api/podcasts.js:51:18 but was not returned from it
This is the code of the function in question:
'findPodcastById': (db, podcastId, callback) => {
var queryString = "SELECT * FROM podcasts WHERE id=$1;";
db.one(queryString, [podcastId])
.then((result) => {
return callback(null, result);
})
.catch((err) => {
return callback(err, null);
});
},
And the parent function that it's called from:
app.post('/getepisodes', (req, res, next) => {
var podcastId = req.body.podcastId;
var userId = req.body.userId;
var podcast;
podcasts.findPodcastByIdAsync(db, podcastId)
.then((result) => {
podcast = result;
return request(podcast.rss);
})
.then((result) => {
return podcastParser.parseAsync(result, {})
})
.then((result) => {
return Promise.resolve(result.channel.items);
})
.map((item) => {
var date = new Date(item.pubDate).toLocaleString();
return podcasts.addEpisodeAsync(db, podcast.id, item.title, item.enclosure.url.split('?')[0], striptags(item.description), date, item.duration);
})
.map((episode) => {
return posts.addPostAsync(db, 'podcast', episode.id, episode.title, episode.description);
})
.then(() => {
return podcasts.findEpisodesByPodcastIdAsync(db, podcastId, userId);
})
.then((result) => {
return res.json(result);
})
.catch((err) => {
next(err);
});
});
I have a return statement in each promise block, so I'm not sure what I'm doing wrong, I would really appreciate some help!
findPostCastBy id is not returning the promise, try this
'findPodcastById': (db, podcastId) => {
return db.one("SELECT * FROM podcasts WHERE id=$1;", [podcastId])
}
I've got a simple function such as;
module.exports = {
fetchUser:function(myUserId) {
return new Promise((resolve, reject) => {
this.getUser(myUserId)
.then(user => {
// do logic // then return user
return user;
})
.then(resolve)
.catch(err => {
// whoops there has been an error
let error = { error: 'My Error' };
reject(error);
});
});
}
};
I want to unit test both the resolve and reject result.
A simple chai test would be;
var expect = require('chai').expect;
var user = require('./user');
describe('User module', function() {
it('test fetchUser', function() {
let _user = user.fetchUser('abc123');
return _user
.then(user => {
expect(data).to.be.an('object');
});
});
Using sinon or another library, how can I for the fetchUser function to throw that reject error?
With Mocha, Chai and Sinon it can be implemented with stubbed method getUser.
const User = require("./fetchUserModule");
describe('User module', () => {
beforeEach(() => User.getUser = sinon.stub());
afterEach(() => User.getUser.reset());
it('returns user if `getUser` returns data', () => {
const user = {name: 'John'};
User.getUser.withArgs("abc123").returns(Promise.resolve(user));
return User.fetchUser("abc123").then(result => {
expect(result).to.equal(user)
}).catch(error => {
expect(error).to.be.undefined;
})
});
it('throws error if `getUser` is rejected', () => {
User.getUser.withArgs("abc123").returns(Promise.reject());
return User.fetchUser("abc123").then(result => {
expect(result).to.be.undefined;
}).catch(err => {
expect(err).to.eql({error: 'My Error'})
})
});
});
Start with anything in your "logic" that can throw an error.
If not you would need to stub this.getUser to reject or throw an error instead of returning data. sinon-as-promised patches sinon.stub to include the .resolves and .rejects promise helpers.
const sinon = require('sinon')
require('sinon-as-promised')
Setup the stub for the failure tests.
before(function(){
sinon.stub(user, 'getUser').rejects(new Error('whatever'))
})
after(function(){
user.getUser.restore()
})
Then either catch the .fetchUser error or use chai-as-promised for some sugar.
it('test fetchUser', function() {
return user.fetchUser('abc123')
.then(()=> expect.fail('fetchUser should be rejected'))
.catch(err => {
expect(err.message).to.eql('whatever')
})
})
it('test fetchUser', function() {
return expect(user.fetchUser('abc123')).to.be.rejectedWith(Error)
})
or async if you live in the new world
it('test fetchUser', async function() {
try {
await user.fetchUser('abc123')
expect.fail('fetchUser should be rejected'))
} catch(err) {
expect(err.message).to.eql('whatever')
}
})
As a side note, you don't need to wrap something that already returns a promise in new Promise and be careful about losing error information when chaining multiple .catch handlers.
fetchUser: function (myUserId) {
return this.getUser(myUserId)
.then(user => {
//logic
return user
})
.catch(err => {
let error = new Error('My Error')
error.original = err
reject(error)
});
}