Can't promisify callback based function - javascript

I want to use the library astro-js where a typical call in their docs looks like this:
const aztroJs = require("aztro-js");
//Get all horoscope i.e. today's, yesterday's and tomorrow's horoscope
aztroJs.getAllHoroscope(sign, function(res) {
console.log(res);
});
For several reasons, I would like to use it using async/await style and leverage try/catch. So I tried promisify like this:
const aztroJs = require("aztro-js");
const {promisify} = require('util');
const getAllHoroscopeAsync = promisify(aztroJs.getAllHoroscope);
async function handle() {
let result, sign = 'libra';
try {
result = await getAllHoroscopeAsync(sign);
}
catch (err) {
console.log(err);
}
console.log("Result: " + result);
}
However, when I log result it comes as undefined. I know the call worked since the library is automatically logging a response via console.log and I see a proper response in the logs.
How can I "await" on this call? (even by other means if this one is not "promisifyable")

util.promisify() expects the callback function to accept two arguments, the first is an error that must be null when there is no error and non-null when there is an error and the second is the value (if no error). It will only properly promisify a function if the callback follows that specific rule.
To work around that, you will have to manually promisify your function.
// manually promisify
aztroJs.getAllHoroscopePromise = function(sign) {
return new Promise(resolve => {
aztroJs.getAllHoroscope(sign, function(data) {
resolve(data);
});
});
};
// usage
aztroJs.getAllHoroscopePromise(sign).then(results => {
console.log(results);
});
Note, it's unusual for an asynchronous function that returns data not to have a means of returning errors so the aztroJs.getAllHoroscope() interface seems a little suspect in that regard.
In fact, if you look at the code for this function, you can see that it is making a network request using the request() library and then trying to throw in the async callback when errors. That's a completely flawed design since you (as the caller) can't catch exceptions thrown asynchronously. So, this package has no reasonable way of communicating back errors. It is designed poorly.

Try custom promisified function
aztroJs.getAllHoroscope[util.promisify.custom] = (sign) => {
return new Promise((resolve, reject) => {
aztroJs.getAllHoroscope(sign, resolve);
});
};
const getAllHoroscopeAsync = util.promisify(aztroJs.getAllHoroscope);

You could change your getAllHoroscopeAsync to a promise function
Example:
const getAllHoroscopeAsync = (sign) =>
new Promise(resolve =>
aztroJs.getAllHoroscope(sign, (res) => resolve(res)));

Related

Redis is outputting true instead of the desired value

I have nodejs running and I want to call this function:
function name(id) {
var x = rclient.get(id, function(err, reply) {
return reply;
});
return x;
}
however when I try to get the output of the function with console.log(name(1)) the output is true, instead of the value stored on the redis server. this seems like a simple thing to fix, however, it has me stumped.
Well you're using callbacks so the return value inside your callback function won't be returned to x.
Try this (depending on your redis client, I've assumed you use node-redis):
function name(id) {
return new Promise((resolve, reject) => {
rclient.get(id, function (err, reply) {
if (err) {
return reject(err);
}
resolve(reply);
});
});
}
// call with
name(1).then((value) => console.log(value)).catch((err) => console.log(err));
// better yet (inside async function)
async main() {
const value = await name(1);
}
Or, do yourself a favour and use handy-redis (https://www.npmjs.com/package/handy-redis):
async function name(id) {
return rclient.get(id);
}
// call with the same as above
Essentially, you're getting slightly confused with async/sync calls. The fact x resolves to true is likely the implementation of the .get method, and not the callback.
instead of the value stored on the redis server. this seems like a simple thing to fix, however, it has me stumped.
I felt like you when I first started with Node.js, it's odd compared to most languages, however, you'll soon find it more natural (especially with async/await syntax)

Meteor.call with await/promise doesn't seem to synchronous

I've been trying to call meteor.call in synchronous way. I tried to follow the example given in the meteor forum doesn't seem to work.
const callWithPromise = (method, myParameters) => {
return new Promise((resolve, reject) => {
Meteor.call(method, myParameters, (err, res) => {
if (err) reject('Something went wrong');
resolve(res);
});
});
}
The following function diiidn't wait for the above and doStuff() returned immediateley after call.
async function doStuff() {
const myValue1 = await callWithPromise('myMethod1', someParameters);
const myValue2 = await callWithPromise('myMethod2', myValue1);
}
Any Input in the above is appreciated.
Yes, await doesn't exactly do what you might expect (or want). And then it needs to be contained in an async function to be used :( It is still dealing with promises, and you need to be aware of that.
Meteor does, however, provide a Promise implementation that allows one to wait without needing to be inside an async function.
import { Promise } from 'meteor/promise'
and in your code you can do this:
const result = Promise.await(Metor.call('server-method',params))
result then contains whatever was returned by the Meteor method, so it behaves like a regular function call.
As Bergi already pointed out, even an async function will return right away -- as the name suggests! -- even if inside of it you are allowed to await things. So you'll need to await on the async function, too. Here is a simplified example:
const wait = () => { return new Promise((resolve, reject) => setTimeout(resolve, 2000)); }
async function doit() { await wait(); console.log('waited'); }
doit(); console.log('the end');
will result in:
the end
waited
however:
await doit(); console.log('the end');
will do what you seem to want, namely:
waited
the end

How to avoid an infinite loop in JavaScript

I have a Selenium webdriverIO V5 framework. The issue I am facing here is, the below code works fine on Mac OS, but it does not work correctly on the Windows OS. In the Windows OS it gets stuck with an infinite loop issue.
The below code functionality is: Merge yaml files (which contains locators) and return the value of the locator by passing the key:
const glob = require('glob');
const yamlMerge = require('yaml-merge');
const sleep = require('system-sleep');
let xpath;
class Page {
getElements(elementId) {
function objectCollector() {
glob('tests/wdio/locators/*.yml', function (er, files) {
if (er) throw er;
xpath = yamlMerge.mergeFiles(files);
});
do {
sleep(10);
} while (xpath === undefined);
return xpath;
}
objectCollector();
return xpath[elementId];
}
}
module.exports = new Page();
Since you are waiting on the results of a callback, I would recommend returning a new Promise from your getElements function and resolve() the value you receive inside the callback. Then when you call getElements, you will need to resolve that Promise or use the await notation. The function will stop at that point and wait until the Promise resolves, but the event loop will still continue. See some documentation for more information.
I'll write an example below of what your code might look like using a Promise, but when you call getElements, you will need to put the keyword await before it. If you want to avoid that, you could resolve the Promise from objectCollector while you're in getElements and remove the async keyword from its definition, but you really should not get in the way of asynchronous JavaScript. Also, you can probably shorten the code a bit because objectCollector looks like an unnecessary function in this example:
const glob = require('glob')
const yamlMerge = require('yaml-merge')
const sleep = require('system-sleep')
let xpath
class Page {
function async getElements(elementId) {
function objectCollector() {
return new Promise((resolve,reject) => {
glob('tests/wdio/locators/*.yml', function (er, files) {
if (er) reject(er)
resolve(yamlMerge.mergeFiles(files))
})
})
}
let xpath = await objectCollector()
return xpath[elementId]
}
}
module.exports = new Page();

React Native AsyncStorage returns promise instead of value

I understand this is a very common problem in RN and I am still trying to understand the very possible advantage of returning a promise when loading data from a property file instead of just returning the value, which makes chaining requests very cumbersome...but anyway. Here is what I have right now, which is a wrapper from the AsyncStorage RN implementation:
multiGet = async (key) => {
var value = null;
try {
value = await AsyncStorage.multiGet(key).then(
(values) => {
value = values;
console.log('Then: ',values);
});
} catch (error) {
console.log('Error: ',error);
}
console.log('Final: ',value);
return value;
}
At this point, value gets undefined. In my main code I have this:
var filter = this._getFilter();
console.log('returned filter:',filter);
The _getFilter function is the one using the AsyncStorage wrapper but the 'returned filter' is logging before the first function so it is not waiting for the returned values before continue, so I get an undefined value.
At first, I thought that just by using the async/await the AsyncStorage wold return a value instead of a promise but after testing, the value I get from:
value = await AsyncStorage.getItem('key')
is STILL a promise, so I have to use then() to read the value.
Basically the order that I am seeing in the logs is:
_getFilter
returned value: undefined
Then: value: here I get the correct value from the keys but the code already passed and I don't have the correct value in the variable
I have no clue what is going on or how to handle this correctly. This is supposed to be very simple and common use case.
I would love to solve this without using a third party module.
Thanks
SOLUTION
Edit: After understanding a little more about the concepts of async/await and callbacks, I finally have a code that works. I don't like it, because it makes the code very hard to read. I might need to refactor it to use promises but for now, it works. Here are some snippets in case someone finds the same issue:
this._getFilter(body,this._filterSearchCallback,callback);
Note: I am sending the body through the chain because I am "completing" the information as I pass the functions. The second parameter is the first callback that actually makes a fetch query and the third callback is the return of the fetch function.
_getFilter(body,callback,returnCallback){
{...}
this._sh.multiGet(keysBanks).then(
(banks) => {
filter.banks = banks;
console.log(banks);
this._sh.multiGet(keysCards).then(
(cards) => {
console.log(cards);
filter.credit_cards = cards;
callback(body,filter,returnCallback);
});
}
);
}
Here basically I am chaining a couple of gets because I need several values from the store. This is the part I dont really like. _sh is my StorageHelper which is a wrapper to the AsyncStorage, nothing fancy.
multiGet = async (key) => {
const value = await AsyncStorage.multiGet(key);
return value;
}
Then my very last callback that actually makes the fetch and send the JSON response to the main screen in react native:
_filterSearchCallback(body,filter,callback){
body.filter = filter;
return fetch(apiUrl, {method: 'post', body: JSON.stringify(body)})
.then((response) => response.json())
.then((responseJson) => {
callback(responseJson);
})
.catch((error) => {
console.error(error);
callback(responseJson);
});
}
I will improve this and make it cleaner but for now, it works. Hope it helps others too.
Once upon a time, i was having the same problem so what I did I will share with you here.
Basically, your execution is moving forward without taking any value i.e undefined what you are getting right now so there are 3-4 ways to get out of this:
1) async await
2) callback
1) We will start with the callback which is use by most of the people.
We will use your code to implement this:
_getFilter(key,callback)
{
multiGet = (key) => {
var collect;
try {
var value = AsyncStorage.multiGet(key).then(
(values) => {
// value = values;
console.log('Then: ',values);
callback(values)
});
} catch (error) {
console.log('Error: ',error);
}
console.log('Final: ',value);
}
}
this._getFilter(key,function(filter){
console.log('returned filter:',filter);
});
2)async/await
If you are using await alone then you would get an error, to use await inside a function you have to declare the async function by setting async keyword before the function name.
async _getFilter(key)
{
multiGet = async (key) => {
var value,collect;
try {
value = await AsyncStorage.multiGet(key).then(
(values) => {
collect= values;
console.log('Then: ',values);
});
} catch (error) {
console.log('Error: ',error);
}
console.log('Final: ',value);
return collect;
}
//calling the async function
this._getFilter(key).then((filter)=>{
if(filter!=null)
console.log('returned filter:',filter)
else
console.log('error')
})
Hope this would clear your concepts and help you with other react native developers.I have seen lots of people struggling with this thing so today I got the chance to clear your doubts.
Cheers :)
the thing is await turns the promise into a value, you don't need to use .then(). Try the following:
const keys = await AsyncStorage.getAllKeys()
const values = await AsyncStorage.multiGet(keys)
// at this point `values` will have data for all keys

JS Promise - instantly retrieve some data from a function that returns a Promise

Can anyone recommend a pattern for instantly retrieving data from a function that returns a Promise?
My (simplified) example is an AJAX preloader:
loadPage("index.html").then(displayPage);
If this is downloading a large page, I want to be able to check what's happening and perhaps cancel the process with an XHR abort() at a later stage.
My loadPage function used to (before Promises) return an id that let me do this later:
var loadPageId = loadPage("index.html",displayPage);
...
doSomething(loadPageId);
cancelLoadPage(loadPageId);
In my new Promise based version, I'd imagine that cancelLoadPage() would reject() the original loadPage() Promise.
I've considered a few options all of which I don't like. Is there a generally accepted method to achieve this?
Okay, let's address your bounty note first.
[Hopefully I'll be able to grant the points to someone who says more than "Don't use promises"... ]
Sorry, but the answer here is: "Don't use promises". ES6 Promises have three possible states (to you as a user): Pending, Resolved and Rejected (names may be slightly off).
There is no way for you to see "inside" of a promise to see what has been done and what hasn't - at least not with native ES6 promises. There was some limited work (in other frameworks) done on promise notifications, but those did not make it into the ES6 specification, so it would be unwise of you to use this even if you found an implementation for it.
A promise is meant to represent an asynchronous operation at some point in the future; standalone, it isn't fit for this purpose. What you want is probably more akin to an event publisher - and even that is asynchronous, not synchronous.
There is no safe way for you to synchronously get some value out of an asynchronous call, especially not in JavaScript. One of the main reasons for this is that a good API will, if it can be asynchronous, will always be asynchronous.
Consider the following example:
const promiseValue = Promise.resolve(5)
promiseValue.then((value) => console.log(value))
console.log('test')
Now, let's assume that this promise (because we know the value ahead of time) is resolved synchronously. What do you expect to see? You'd expect to see:
> 5
> test
However, what actually happens is this:
> test
> 5
This is because even though Promise.resolve() is a synchronous call that resolves an already-resolved Promise, then() will always be asynchronous; this is one of the guarantees of the specification and it is a very good guarantee because it makes code a lot easier to reason about - just imagine what would happen if you tried to mix synchronous and asynchronous promises.
This applies to all asynchronous calls, by the way: any action in JavaScript that could potentially be asynchronous will be asynchronous. As a result, there is no way for you do any kind of synchronous introspection in any API that JavaScript provides.
That's not to say you couldn't make some kind of wrapper around a request object, like this:
function makeRequest(url) {
const requestObject = new XMLHttpRequest()
const result = {
}
result.done = new Promise((resolve, reject) => {
requestObject.onreadystatechange = function() {
..
}
})
requestObject.open(url)
requestObject.send()
return requestObject
}
But this gets very messy, very quickly, and you still need to use some kind of asynchronous callback for this to work. This all falls down when you try and use Fetch. Also note that Promise cancellation is not currently a part of the spec. See here for more info on that particular bit.
TL:DR: synchronous introspection is not possible on any asynchronous operation in JavaScript and a Promise is not the way to go if you were to even attempt it. There is no way for you to synchronously display information about a request that is on-going, for example. In other languages, attempting to do this would require either blocking or a race condition.
Well. If using angular you can make use of the timeout parameter used by the $http service if you need to cancel and ongoing HTTP request.
Example in typescript:
interface ReturnObject {
cancelPromise: ng.IPromise;
httpPromise: ng.IHttpPromise;
}
#Service("moduleName", "aService")
class AService() {
constructor(private $http: ng.IHttpService
private $q: ng.IQService) { ; }
doSomethingAsynch(): ReturnObject {
var cancelPromise = this.$q.defer();
var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });
return { cancelPromise: cancelPromise, httpPromise: httpPromise };
}
}
#Controller("moduleName", "aController")
class AController {
constructor(aService: AService) {
var o = aService.doSomethingAsynch();
var timeout = setTimeout(() => {
o.cancelPromise.resolve();
}, 30 * 1000);
o.httpPromise.then((response) => {
clearTimeout(timeout);
// do code
}, (errorResponse) => {
// do code
});
}
}
Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.
If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?
You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve and reject methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout that may give you what you want without workarounds.
let xhrRequest = (path, data, method, success, fail) => {
const xhr = new XMLHttpRequest();
// could alternately be structured as polymorphic fns, YMMV
switch (method) {
case 'GET':
xhr.open('GET', path);
xhr.onload = () => {
if (xhr.status < 400 && xhr.status >= 200) {
success(xhr.responseText);
return null;
} else {
fail(new Error(`Server responded with a status of ${xhr.status}`));
return null;
}
};
xhr.onerror = () => {
fail(networkError);
return null;
}
xhr.send();
return null;
}
return xhr;
case 'POST':
// etc.
return xhr;
// and so on...
};
// can work with any function that can take success and fail callbacks
class CancellablePromise {
constructor (fn, ...params) {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
fn(...params, this.resolve, this.reject);
return null;
});
}
};
let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');
p.promise.then(loadPage).catch(handleError);
// times out after 2 seconds
setTimeout(() => { p.reject(new Error('timeout')) }, 2000);
// for an alternative version that simply tells the user when things
// are taking longer than expected, NOTE this can be done with vanilla
// promises:
let timeoutHandle = setTimeout(() => {
// don't use alert for real, but you get the idea
alert('Sorry its taking so long to load the page.');
}, 2000);
p.promise.then(() => clearTimeout(timeoutHandle));
Promises are beautiful. I don't think there is any reason that you can not handle this with promises. There are three ways that i can think of.
The simplest way to handle this is within the executer. If you would like to cancel the promise (like for instance because of timeout) you just define a timeout flag in the executer and turn it on with a setTimeout(_ => timeout = true, 5000) instruction and resolve or reject only if timeout is false. ie (!timeout && resolve(res) or !timeout && reject(err)) This way your promise indefinitely remains unresolved in case of a timeout and your onfulfillment and onreject functions at the then stage never gets called.
The second is very similar to the first but instead of keeping a flag you just invoke reject at the timeout with proper error description. And handle the rest at the then or catch stage.
However if you would like to carry the id of your asych operation to the sync world then you can also do it as follows;
In this case you have to promisify the async function yourself. Lets take an example. We have an async function to return the double of a number. This is the function
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
We would like to use promises. So normally we need a promisifier function which will take our async function and return another function which when run, takes our data and returns a promise. Right..? So here is the promisifier function;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
Lets se how they work together;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
var doubleWithPromise = promisify(doubleAsync);
doubleWithPromise(100).then(v => console.log("The asynchronously obtained result is: " + v));
So now you see our doubleWithPromise(data) function returns a promise and we chain a then stage to it and access the returned value.
But what you need is not only a promise but also the id of your asynch function. This is very simple. Your promisified function should return an object with two properties; a promise and an id. Lets see...
This time our async function will return a result randomly in 0-5 secs. We will obtain it's result.id synchronously along with the result.promise and use this id to cancel the promise if it fails to resolve within 2.5 secs. Any figure on console log Resolves in 2501 msecs or above will result nothing to happen and the promise is practically canceled.
function promisify(fun){
return function(data){
var result = {id:null, promise:null}; // template return object
result.promise = new Promise((resolve,reject) => result.id = fun(data, (err,res) => err ? reject(err) : resolve(res)));
return result;
};
}
function doubleAsync(data,cb){
var dur = ~~(Math.random()*5000); // return the double of the data within 0-5 seconds.
console.log("Resolve in " + dur + " msecs");
return setTimeout(_ => cb(false, data*2),dur);
}
var doubleWithPromise = promisify(doubleAsync),
promiseDataSet = doubleWithPromise(100);
setTimeout(_ => clearTimeout(promiseDataSet.id),2500); // give 2.5 seconds to the promise to resolve or cancel it.
promiseDataSet.promise
.then(v => console.log("The asynchronously obtained result is: " + v));
You can use fetch(), Response.body.getReader(), where when .read() is called returns a ReadableStream having a cancel method, which returns a Promise upon cancelling read of the stream.
// 58977 bytes of text, 59175 total bytes
var url = "https://gist.githubusercontent.com/anonymous/"
+ "2250b78a2ddc80a4de817bbf414b1704/raw/"
+ "4dc10dacc26045f5c48f6d74440213584202f2d2/lorem.txt";
var n = 10000;
var clicked = false;
var button = document.querySelector("button");
button.addEventListener("click", () => {clicked = true});
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var len = 0;
reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
return "stream complete"
});
};
if (!clicked) {
len += result.value.byteLength;
}
// cancel stream if `button` clicked or
// to bytes processed is greater than 10000
if (clicked || len > n) {
return reader.cancel().then(function() {
return "read aborted at " + len + " bytes"
})
}
console.log("len:", len, "result value:", result.value);
return reader.read().then(processData)
})
.then(function(msg) {
alert(msg)
})
.catch(function(err) {
console.log("err", err)
})
});
<button>click to abort stream</button>
The method I am currently using is as follows:
var optionalReturnsObject = {};
functionThatReturnsPromise(dataToSend, optionalReturnsObject ).then(doStuffOnAsyncComplete);
console.log("Some instant data has been returned here:", optionalReturnsObject );
For me, the advantage of this is that another member of my team can use this in a simple way:
functionThatReturnsPromise(data).then(...);
And not need to worry about the returns object. An advanced user can see from the definitions what is going on.

Categories

Resources