I'm using node-strava-v3 with Node 5.7 to retrieve JSON collections from Strava's API. Pretty simple actually, no problem actually.
I just want to take the opportunity to try the ES6 Generators. So far so good I think I get the concept. But in the case of this Node-Strava-V3 API wrapper, the methods never return a value or a promise directly. Only Callbacks are allowed.
Which brings me to the question : how can I insert the ES6 Generators concept into play?
Take this : (this requires access_token you can request on the Strava portal)
var strava = require('strava-v3');
var mainActivity = {id: 504641206};
strava.activities.get(mainActivity, function (err, data){
if(err){ console.log("Strava GET issue: ", err);
/// consume the JSON data here...
};
//rest of the code
but can I use the generators to pause until this "strava.activities.get()" method ends? this method does not return any, no value, no promise.
Thanks for all your suggestions
FJ
You could do this with generators and a library like co, but now we have async functions (ES2017, and can be transpiled for outdated environments).
Tangentially related: Any API that provides callbacks can be promise-ified:
const strava = require('strava-v3');
const mainActivity = {id: 504641206};
new Promise((resolve, reject) => {
strava.activities.get(mainActivity, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
}).then(
result => {
//rest of the code
},
error => {
// handle error
}
);
In fact, that common pattern is codified in util.promisify:
const strava = require("strava-v3");
const promisify = require("util").promisify;
const stravaActivitiesGet = promisify(strava.activities.get);
const mainActivity = {id: 504641206};
stravaActivitiesGet(mainActivity).then(
result => {
//rest of the code
},
error => {
// handle error
}
);
There are multiple libraries that promise-ify a whole API at once (rather than per-function), such as node-promisify.
If we use util.promisify and an async function:
const strava = require("strava-v3");
const promisify = require("util").promisify;
const stravaActivitiesGet = promisify(strava.activities.get);
const mainActivity = {id: 504641206};
(async () => {
try {
const data = await stravaActivitiesGet(mainActivity);
//rest of the code
} catch (error) {
// handle error
}
})();
That mostly makes sense if you're doing other asynchronous things in the logic.
Related
In node.js I am trying to get a list of bid and ask prices from a trade exchange website (within an async function). Within the foreach statement I can console.info() the object data(on each iteration) but when I put all of this into an array and then return it to another function it passes as 'undefined'.
const symbolPrice = async() => {
let symbolObj = {}
let symbolList = []
await bookTickers((error, ticker) => {
ticker.forEach(symbol => {
if (symbol.symbol.toUpperCase().startsWith(starts.toUpperCase())) {
symbolObj = {
symbol: symbol.symbol,
bid: symbol.bidPrice,
ask: symbol.askPrice
}
console.info(symbolObj);
}
symbolList.push(symbolObj)
});
const results = Promise.all(symbolList)
return results;
});
}
const symbolPriceTest = async() => {
const res = await symbolPrice(null, 'ETH', true);
console.log(res)
}
I have tried pretty much everything I can find on the internet like different awaits/Promise.all()'s. I do admit I am not as familiar with async coding as I would like to be.
So, if the basic problem here is to call bookTickers(), retrieve the asynchronous result from that call, process it and then get that processed result as the resolved value from calling symbolPrice(), then you can do that like this:
const { promisify } = require('util');
const bookTickersP = promisify(bookTickers);
async function symbolPrice(/* declare arguments here */) {
let symbolList = [];
const ticker = await bookTickersP(/* fill in arguments here */);
for (let symbol of ticker) {
if (symbol.symbol.toUpperCase().startsWith(starts.toUpperCase())) {
symbolList.push({
symbol: symbol.symbol,
bid: symbol.bidPrice,
ask: symbol.askPrice
});
}
}
return symbolList;
}
async function symbolPriceTest() {
const res = await symbolPrice(null, 'ETH', true);
console.log(res)
}
Things to learn from your original attempt:
Only use await when you are awaiting a promise.
Only use Promise.all() when you are passing it an array of promises (or an array of a mixture of values and promises).
Don't mix plain callback asynchronous functions with promises. If you don't have a promise-returning version of your asynchronous function, then promisify it so you do (as shown in my code above with bookTickersP().
Do not guess with async and await and just throw it somewhere hoping it will do something useful. You MUST know that you're awaiting a promise that is connected to the result you're after.
Don't reuse variables in a loop.
Your original implementation of symbolPrice() had no return value at the top level of the function (the only return value was inside a callback so that just returns from the callback, not from the main function). That's why symbolPrice() didn't return anything. Now, because you were using an asynchronous callback, you couldn't actually directly return the results anyway so other things had to be redone.
Just a few thoughts on organization that might be reused in other contexts...
Promisify book tickers (with a library, or pure js using the following pattern). This is just the api made modern:
async function bookTickersP() {
return new Promise((resolve, reject) => {
bookTickers((error, ticker) => {
error ? reject(error) : resolve(ticker);
})
});
}
Use that to shape data in the way that the app needs. This is your app's async model getter:
// resolves to [{ symbol, bid, ask }, ...]
async function getSymbols() {
const result = await bookTickersP();
return result.map(({ symbol, bidPrice, askPrice }) => {
return { symbol: symbol.toUpperCase(), bid: bidPrice, ask: askPrice }
});
}
Write business logic that does things with the model, like ask about particular symbols:
async function symbolPriceTest(search) {
const prefix = search.toUpperCase();
const symbols = await getSymbols();
return symbols.filter(s => s.symbol.startsWith(prefix));
}
symbolPriceTest('ETH').then(console.log);
I'm trying to make an constructor for multiple Redis connections, so i've started to try something.
I'm only getting back from has Promise { }, but if I do an console.log before the return I'm getting the real Value.
EDIT: Tried without async/await still don't work.
app.js
const rBredis = require("./redis");
const redis = new rBredis();
console.log(redis.has("kek"));
redis.js
const Redis = require("ioredis");
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
async has(id) {
return await this.redis.exists(id)
.then( exists => {
// console.log(exists); works 0
return exists; // works not Promise { <pending> }
});
};
}
module.exports = BasicRedis;
I don't understand your question completely but I see a problem here.
You need to brush up your knowledge of Promises and Async await. You either use async
await or Promises (.then) syntax to make it work properly.
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
// You can either do it like this
has(id) {
return new Promise((res, rej) => {
this.redis.exists(id)
.then( exists => {
res(exists)
}).catch(err => {
rej(err.message)
});
})
};
// Or like this
has(id) {
return this.redis.exists(id)
};
}
In both cases, you can await/.then result in your app.js
// app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek").then(res => console.log(res))
EDIT - 1
If this is something that'd take time even 1 millisecond there's no way you're going to get the value right away. You need to use either async-await or promises. Or use a callback like this
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
has(id, callback) {
this.redis.exists(id)
.then( exists => {
callback(exists)
}).catch(err => {
callback(err.message)
});
};
}
app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek", (res) => console.log(res))
Here's reference to Promises MDN and Async Await MDN
Hope it helps.
I'm writting some library that will provide asynchronous methods, I want the user to be able to use classic callbacks or modern promises depending on its own preference.
I was coding two versions of each methods by naming them 'myMethod' and 'myMethodPromise', then a thought came accross my mind :
Why not coding a method that combines the two patterns?
One single method with an optional callback argument, if not provided then the method returns a promise instead of calling the callback.
Would it be a good practice?
// Promise-callback combined pattern method
function myAsyncMethod ( callback = null ) {
if(callback) {
var result = "xxx";
// Do something...
callback(result);
} else {
return(new Promise((res, rej) => {
var result = "xxx";
// Do something...
res(result);
}))
}
}
// Usage with callback
myAsyncMethod((result)=>document.getElementById('callbackSpan').innerHTML = result);
// or with promise
myAsyncMethod().then((result) => document.getElementById('promiseSpan').innerHTML = result);
<p>
Result with callback : <span id="callbackSpan"></span>
</p>
<p>
Result with promise : <span id="promiseSpan"></span>
</p>
**
You can follow request-promise module. They have wrape request module inside request-promise. And as only written small config file to assign all promisable methods.
More: https://github.com/request/request-promise/blob/master/lib/rp.js
You can also use node-promisify
to promisify method.
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
stat('.').then((stats) => {
// Do something with `stats`
}).catch((error) => {
// Handle the error.
});
// Request module
Bluebird.config({cancellation: true});
configure({
request: request,
PromiseImpl: Bluebird,
expose: [
'then',
'catch',
'finally',
'cancel',
'promise'
// Would you like to expose more Bluebird methods? Try e.g. `rp(...).promise().tap(...)` first. `.promise()` returns the full-fledged Bluebird promise.
],
constructorMixin: function (resolve, reject, onCancel) {
var self = this;
onCancel(function () {
self.abort();
});
}
});
request.bindCLS = function RP$bindCLS() {
throw new Error('CLS support was dropped. To get it back read: https://github.com/request/request-promise/wiki/Getting-Back-Support-for-Continuation-Local-Storage');
};
i had try to test out an encryption stuff and im new to nodejs.
after several try and search over google, i unable to solve my problem.
please help.
case: calling async method to encrypt data, however it return me with a Promise { <pending> }
im using npm openpgp
objective: return the ciphertext so i could use it for other purpose
my code as below:
//execution.js
var tools = require('./tools');
console.log(tools.encrypt());
//tools.js
const openpgp = require('openpgp') // use as CommonJS, AMD, ES6 module or via window.openpgp
var fs = require('fs');
openpgp.initWorker({ path:'openpgp.worker.js' }) // set the relative web worker path
var pubkey = fs.readFileSync('public.key', 'utf8');
const passphrase = `super long and hard to guess secret` //what the privKey is encrypted with
module.exports = {
encrypt:async () =>{
const options = {
message: openpgp.message.fromText('Hello, World!'), // input as Message object
publicKeys: (await openpgp.key.readArmored(pubkey)).keys, // for encryption
}
const encrypted = await openpgp.encrypt(options);
const ciphertext = encrypted.data;
fs.writeFile('message.txt',ciphertext ,'utf8', function (err) {
if (err) throw err;
console.log('msg written!');
});
return ciphertext;
},
decrypt: async function(){
// your code here
}
};
please help
Async Await is simply syntactic sugar for promises an async function returns a promise.
You can't use await at the top level. What you can do is:
(async () => {
try {
console.log(await tools.encrypt());
} catch (e) {
console.log(e);
}
})();
// using promises
tools.encrypt().then(console.log).catch(console.log);
tools.encrypt().then(res => console.log(res))
this line from #mark meyer solve my problem.
i was trying to access the thing without have to declare the 'async' word and have access to the 'res' so i could use for other purpose
Thanks alot.
UPDATE
I just realized something fundamentally wrong in this approach and that nested callbacks can't return something to its parent callback. I came in late in the JS world and come from the Promises era and didn't know this is the problem with callbacks. But I didn't see enough examples for Meteor using promises so I used callbacks instead. However, if this code can be improved I'd appreciate it greatly.
Question
So I'm calling a method from the client using:
Meteor.call('cart.useProfileAddress', {}, (error, address) => {
console.info('Address', address) // this returns undefined on client
})
This is the method in my api/carts/cartsMethod.js
export const useProfileAddress = new ValidatedMethod({
name: 'cart.useProfileAddress',
validate(args) {
//
},
run(args) {
const person = Persons.findOne({'userId': Meteor.userId()});
// If I do the return here I get the address in the browser as defined.
// return person.address
// I'm calling another method under here:
getClosestStore.call({address: person.address}, (error, result) => {
// And another one method call here:
updateCartAddress.call({address: person.address}, (error, result) => {
// So once all the callbacks are done return the address here.
// However the problem is I get `undefined` on the client.
if (!error) {
// console displays something on the Server but is `undefined` on the Client
console.info('Returning Address', person.address)
return person.address
}
})
})
}
})
What could be the problem on the code above? Could it be because I'm trying to get the value from a nested callback?
Also does anyone know how to avoid these nested callbacks? I know how to do it on Node using promises but in Meteor (I'm using 1.4) I'm still clueless.
Methods can run synchronously on server so you do not need to use callback. Result of method will be returned after execution or exception will be thrown if error occurs. Try this:
export const useProfileAddress = new ValidatedMethod({
// ...
run(args) {
const person = Persons.findOne({'userId': Meteor.userId()});
const result1 = getClosestStore.call({address: person.address});
// use result1 if you need to
const result2 = updateCartAddress.call({address: person.address});
// // use result2 if you need to
return person.address;
}
})
This is how I solved my problem using Promise and the new async/await feature of Meteor 1.3+
export const useProfileAddress = new ValidatedMethod({
name: 'cart.useProfileAddress',
validate(args) {
//
},
run(args) {
return ((async () => {
const person = Persons.findOne({'userId': Meteor.userId()});
const storeId = await getClosestStore.callPromise({address: person.address})
const newAddress = await updateCartAddress.callPromise({address: person.address})
return newAddress
})())
}
})
Inside each method I used the didericis:callpromise-mixin so that it will return a promise.