I could get this function to work in one file, but I'd like to keep things clean by separating into multiple files.
// library.js file
module.exports = {
get: () =>{
return new Promise((reject, resolve) =>{
return resolve(https.get('https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token, (res) =>{
res.setEncoding('utf8');
return res.on('data', (data) =>{
return data;
});
}));
});
}
}
I tried logging this but it didn't show anything?
// server.js file
igLib.get().then((data) => {
console.log("testing: " + data);
})
However, if I just do a simple log of..
// server.js file
console.log(igLib.get());
I somehow get the data without the res.setEncoding('utf8').
Any tips on what to do?
Update:
I couldn't get the promise to work and I'm sorry to the folks who provided me good answers to my problem, but I went ahead and used the request-promise module instead. This is how it looks like:
// library.js file
var instagramSelfUrl = 'https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token;
module.exports = {
get: () =>{
return rp(instagramSelfUrl).then((res) =>{
return res;
});
}
}
And here is where I console.log:
// server.js file
igLib.get().then((data) =>{
console.log(data);
});
It's simpler and it works. If there's a solution to the problem other than using a module for this to work, please let it be known and post! Thank you all to who posted and helped out!
The promise you create should resolve with the data from the response.
// library.js file
const https = require('https');
const instagramSelfUrl = 'https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token;
module.exports = {
get: () => new Promise(function (resolve, reject) {
https.get(instagramSelfUrl, res => {
var chunks = [];
res.setEncoding('utf8');
res.on('data', chunk => chunks.push(chunk));
res.on('end', () => resolve(chunks.join('')));
});
})
};
In fact, the above is incomplete and brittle. For example, request errors or response status 500 should reject the promise, but there is no error handling. Or when the response is not really UTF-8 it will decode the data improperly. And those are only the two most obvious issues here.
The promisification of HTTP requests is a solved problem. I strongly suggest that you use one of the available libraries instead of rolling your own code that makes all the mistakes all over again.
const request = require('request-promise');
const instagramSelfUrl = 'https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token;
module.exports = {
get: () => request(instagramSelfUrl)
};
Now the function is complete and behaves properly, even in corner cases. It also is so short that it hardly justifies writing a separate function at all.
It seems you aren't return the data correctly try this:
// library.js file
module.exports = {
get: () =>{
return new Promise((reject, resolve) =>{
https.get('https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token, (res) =>{
res.setEncoding('utf8');
return res.on('data', (data) =>{
return resolve(data);
});
});
});
}
}
You can do something like this
module.exports = {
get: () =>{
return new Promise((resolve, reject) =>{
https.get('https://api.instagram.com/v1/users/self/?access_token=' + cred.access_token, (res) =>{
res.setEncoding('utf8');
res.on('data', (data) =>{
resolve(data);
});
}).on('error', (e) => reject(e))
});
}
}
For more on promise you can visit https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
Related
I am sending data to a Bluetooth device, and the responses are handled by a listener that's set up during the connection process:
device.connect().then(device => {
device.registerResponseListener((data) => {
// handle response
}
}
I have a separate function that sends data to the device:
const sendData = (device, data) => {
device.write(data);
}
My question is, how can I Promisify this code? I'd like to be able to do
const sendData = (device, data) => {
return new Promise((resolve, reject) => {
device.write(data);
// resolve...?
});
}
But how do I get the resolve into the Bluetooth response listener?
I don't know what API you're using but you can try BluetoothRemoteGATTCharacteristic API. It has writeValueWithResponse method which return Promise.
https://developer.mozilla.org/en-US/docs/Web/API/BluetoothRemoteGATTCharacteristic
If I understood you correctly then you can do it like this
const sendData = async (device, data) => {
const response = await device.write(data);
await Promise.all(device.responseListeners.map(listener => listener(response)))
}
The best possible solution in this case, while still not ideal, was to store the resolve function in variable at a higher scope:
var sendDataResolve;
device.connect().then(device => {
device.registerResponseListener((data) => {
sendDataResolve(data);
}
}
const sendData = (device, data) => {
return new Promise((resolve, reject) => {
sendDataResolve = resolve;
device.write(data);
});
}
...
sendData(device, "data")
.then(result => {
console.log("Got result",result);
});
The caveat is that Promise resolutions are NOT guaranteed to be tied correctly to the original request. This ONLY works with one request at a time.
I am attempting to load some CSV data in my API such that I can manipulate it and pass through to my front end, however I am having a few issues returning the data.
I am using fast-csv to do the parsing here.
service.js
const fs = require('fs');
const csv = require('fast-csv');
module.exports.getFileContents = (filepath) => {
let data = [];
fs.createReadStream(filepath)
.pipe(csv.parse({ headers: true }))
.on('error', error => console.error(error))
.on('data', row => data.push(row))
.on('end', () => {
console.log(data) // This will print the full CSV file fine
return data;
});
};
routes.js
router.get('/data/:filename', (req, res) => {
const file = FS.getFileContents(testUrl + '/' + req.params.filename + '.csv');
console.log(file); // This prints 'undefined'
res.send(file);
});
I can print out the CSV contents fine from the service, but I just get 'undefined' from the actual routes. Can somebody please point out what I'm missing?
This is a common problem with JavaScript code, in the following.
.on('end', () => {
console.log(data);
return data;
});
Your on-end handler is an anonymous callback function (because of () =>), so when you return data, you are returning data out of your on-end handler callback function. You are not returning data out of your enclosing getFileContents() function.
Here's a typical way to write this kind of code:
const getFileContents = async (filepath) => {
const data = [];
return new Promise(function(resolve, reject) {
fs.createReadStream(filepath)
.pipe(csv.parse({ headers: true }))
.on('error', error => reject(error))
.on('data', row => data.push(row))
.on('end', () => {
console.log(data);
resolve(data);
});
});
}
And then, call it as follows, though this must be within an async function:
const data = await getFileContents('games.csv');
What's happened here is as follows:
your getFileContents is now async and returns a promise
the CSV data will be available when resolve(data) is executed
the caller can await the fulfillment/resolution of this promise to get the data
You could just create a Promise in the service and return it. Once the job is done, resolve it. The returned Promise will wait until it is resolved.
service.js
const fs = require('fs');
const csv = require('fast-csv');
module.exports.getFileContents = (filepath) => {
let data = [];
return new Promise((resolve) => {
fs.createReadStream(filepath)
.pipe(csv.parse({ headers: true }))
.on('error', error => console.error(error))
.on('data', row => data.push(row))
.on('end', () => {
resolve(data);
});
}
};
routes.js
router.get('/data/:filename', (req, res) => {
const file = await FS.getFileContents(testUrl + '/' + req.params.filename + '.csv');
console.log(file); // This prints only after it is resolved
res.send(file);
});
I want to mock the result of a function within a node module so that i can run assertions.
Considering the following node module:
const doPostRequest = require('./doPostRequest.js').doPostRequest;
const normalizeSucessResult = require('./normalizer.js').normalizeSucessResult;
const normalizeErrorResult = require('./normalizer.js').normalizeErrorResult;
exports.doPost = (params, postData) => {
return doPostRequest(params, postData).then((res) => {
const normalizedSuccessResult = normalizeSucessResult(res);
return normalizedSuccessResult;
}).catch((err) => {
const normalizedErrorResult = normalizeErrorResult(err);
return normalizedErrorResult;
})
}
The function doPostRequest returns a promise. How can i fake the return value of this promise so that i can assert if normalizeSucessResult has been called?
So for i have tried:
const normalizeSucessResult = require('./normalizer.js');
const doPostRequest = require('./doPostRequests.js');
const doPost = require('./doPost.js');
it('runs a happy flow scenario', async () => {
let normalizeSucessResultStub = sinon.stub(normalizeSucessResult, 'normalizeSucessResult');
let postData = { body: 'Lorum ipsum' };
let params = { host: 'someUrl', port: 433, method: 'POST', path: '/' };
sinon.stub(doPostRequest, 'doPostRequest').resolves("some response data"); //Fake response from doPostRequest
return doPost.doPost(params, postData).then((res) => { //res should be equal to some response data
expect(normalizeSucessResultStub).to.have.been.calledOnce;
expect(normalizeSucessResultStub).to.have.been.with("some response data");
});
});
The doPostRequest module looks like this:
const https = require('https')
module.exports.doPostRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
You can use Promise.resolve to return a promise with any given value.
Promise.resolve(“hello world”);
For stub your func you need to do like this
sinon.stub({doPostRequest}, 'doPostRequest').resolves("some response data")
Okay, i figured it out. The function doPostRequest was loaded using require, on the top of the file using const doPostRequest = require('./doPostRequest.js').doPostRequest;
In order to mock the data that comes back from a function that is loaded using require i had to use a node module called mock-require. There are more modules that can take care of this (proxyquire is a populair one) but i picked mock-require (i did not have a specific reason for choosing mock-require).
For anyone else that is stuck with a similar problem, try mock-require to mock the respose from files that are loaded using require.
I have this function
function getJsonObjectFromURL(url, onData) {
let chunks = [];
return require('https').get(url, res => {
res.setEncoding('utf8')
.on('data', (chunk) => {
chunks.push(chunk);
})
.on('end', () => {
onData(JSON.parse(chunks.join('')));
});
}).on('error', function(e) {
console.log("Got an error: ", e);
});
}
Also I have this script that converts url's data to json array.
url = https://pu.vk.com/c824502/upload.php?act=do_add&mid=213468131&aid=-14&gid=156603484&hash=7ab9a7e723425f4a6ca08709cbd5ebd0&rhash=ba8f0ec6580a6eafce38349b12ed3789&swfupload=1&api=1&wallphoto=1
getJsonObjectFromURL(url, data => {
console.log(data.server, data.photo, data.hash);
});
It goes well when console.log. But when I want to make from this script variable, it gives me huge collection
var xx = getJsonObjectFromURL(url, data => {
return data.server;
});
console.log(xx);
Your function getJsonObjectFromURL() doesn't return the object returned by the URL. It returns the object responsible for the https request code, which is something you don't want.
I see that you are using ES6, so the best solution for you is to probably create an async function that returns a promise, which will give you great flexibility. Here is an improved version of your code:
const https = require('https');
async function getJsonObjectFromURL(url) {
return new Promise((resolve, reject) => {
const chunks = [];
try {
https.get(url, res => {
res.setEncoding('utf8')
.on('data', (chunk) => {
chunks.push(chunk);
})
.on('end', () => {
resolve(JSON.parse(chunks.join('')));
});
}).on('error', e => reject(e));
} catch (err) {
reject(err);
}
});
};
This code allows you to retrieve the remote contents of the HTTPS url synchronously or asynchronously.
Asynchronous Call
As you have already done in your code, you can use a lambda callback that handles the response when it is ready.
const url = 'https://pu.vk.com/c824502/upload.php?act=do_add&mid=213468131&aid=-14&gid=156603484&hash=7ab9a7e723425f4a6ca08709cbd5ebd0&rhash=ba8f0ec6580a6eafce38349b12ed3789&swfupload=1&api=1&wallphoto=1';
// here we use a lambda callback that handles the response
getJsonObjectFromURL(url)
.then(data => {
console.log(data.server, data.photo, data.hash);
})
.catch(err => console.error(err));
Synchronous Call
The synchronous call forces the function to wait for the result. This is how you can do it:
async function getSync() {
try {
// wait for the result
const data = await getJsonObjectFromURL(url);
console.log(data.server);
} catch(err) {
console.error(err);
}
}
getSync();
Please note that we can only use the await keyword when we are inside an async function. This is why I had to wrap the synchronous call with a function.
I am making an app that creates multiple HTTP requests to different APIs, and would like to abstract the "sending" and "getting" portion of the data into a utility function that can be exported. It should be async. So far, I have created the following, but it won't work since request.end() needs to be declared somewhere, and I'm stuck here. Thanks!
Note: the resulting data should be able to be easily returned to some other function on order to do other work with it later on.
exports.handleHttpRequests = (url) => {
return new Promise((resolve, reject) => {
const request = http.request(url, response => {
let chunks = [];
res.on('data', chunk => {
chunks.push(chunk);
});
res.on('end', () => {
let body = Buffer.concat(chunks).toString();
resolve(body);
})
})
}).then(data => {
return data;
});
}
Hey you already done all, just call request.end() inside your new Promise callback & also handle error . And do what you want to do with resolve ,reject.
exports.handleHttpRequests = (url) => {
return new Promise((resolve, reject) => {
const request = http.request(url, res => {
let chunks = [];
res.on('data', chunk => {
chunks.push(chunk);
});
res.on('end', () => {
let body = Buffer.concat(chunks).toString();
resolve(body);
})
});
//handling error
request.on('error', (e) => {
reject('problem with request: ' + e.message);
});
//here you have to put request.end()
request.end();
})
};