Callback is not working in ajax request? - javascript

I am trying to build a contentEditor with draft js. Exactly the feature is Extract the data from url like Facebook. But I am stuck with this part. Callback is not working.
First I wrapped my state with compositeDecorator Like this
constructor(props) {
super(props);
const compositeDecorator = new CompositeDecorator([
.... {
strategy: linkStrategy,
component: decorateComponentWithProps(linkComp, {
passit
})
}
....
]);
}
// This is my strategy
function linkStrategy(contentBlock, callback, contentState) {
findLinkInText(LINK_REGEX, contentBlock, callback)
}
function findLinkInText(regex, contentBlock, callback) {
const text = contentBlock.getText();
let matchArr, start;
if ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index;
let URL = matchArr[0];
console.log(URL);
axios.post('/url', {
url: URL
}).then(response => {
passit = response.data
//not working
callback(start, start + URL.length)
})
//working
callback(start, start + URL.length)
}
}
If the callback won't work, the component will not render..
I don't know this is a basic javascript problem. But the thing is I want to fetch the url data from my server and I have to pass the data via props to my component and render it.
UPDATE FOR THE ANSWER
function findLinkInText(regex, contentBlock, callback) {
const text = contentBlock.getText();
let matchArr, start;
if ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index;
let url = matchArr[0];
axios.post('/url', {
url: URL
}).then(response => {
passit = response.data
handleWithAxiosCallBack(start, start + matchArr[0].length, callback)
}).catch(err => console.log(err))
}
}
function handleWithAxiosCallBack(start, startLength, callback) {
console.log(callback); //Spits out the function But Not working
callback(start, startLength)
}

The below described technique would help you to achieve the behaviour you are expecting.
Why your solution does not works: The reason the desired action that needs to be performed by the callback is not performing is because, draft expects the callback to be called synchronous. Since you are using an async function(the axios api call) and asynchronously calling the callback has no effect.
Solution: This may not be a efficient solution, but could get the job done. In simple words, all you have to do is to store the results from the axios call in a variable(temporarily) and then trigger the re-render for your editor, retrieve the result store earlier and use it to call the callback.
I'm following based on this example here. Assuming that you store the editor state in the component's state. Below is a pseudo-code which you might need to implement as per your needs.
Let's assume your component state is like below which holds the Editor's state.
constructor(props){
super(props);
// .. your composite decorators
// this is your component state holding editors state
this.state = {
editorState: EditorState.createWithContent(..)
}
// use this to temporarily store the results from axios.
this.tempResults = {};
}
Assuming you are rendering your Editor to something like below. Notice the ref. Here the editors reference is stored in the component's editor variable which you can access later. Using a string as ref would work, but this is the recommended way to store refs.
<Editor
ref={ (editor) => this.editor }
editorState={this.state.editorState}
onChange={this.onChange}
// ...your props
/>
In your component, write a function to update the editor with currentState which would force the re-render. Make sure this function is bound to your component so that we get the correct this(context).
forceRenderEditor = () => {
this.editor.update(this.state.editorState);
}
In your findLinkInText function do the below.
First make sure it(findLinkInText) is bound to your component so we get the correct this. You can use arrow function to do this or bind it in the component constructor.
Second, check if we have the result for the url already in tempResults
which we declared in the component's constructor. If we have one, then call the callback immediately with appropriate arguments.
If we don't have a result already, the make the call and store the result in the tempResults. After storing, call the already defined this.forceRenderEditor method which would invoke draft to re-check and this time, since we have stored the results in the tempResults, the callback will be called and appropriate changes would reflect.
function findLinkInText(regex, contentBlock, callback) {
const text = contentBlock.getText();
let matchArr, start;
if ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index;
let URL = matchArr[0];
console.log(URL);
// do we have the result already,??
// call the callback based on the result.
if(this.tempResults[url]) {
// make the computations and call the callback() with necessary args
} else {
// if we don't have a result, call the api
axios.post('/url', {
url: URL
}).then(response => {
this.tempResults[url] = response.data;
this.forceRenderEditor();
// store the result in this.tempResults and
// then call the this.forceRenderEditor
// You might need to debounce the forceRenderEditor function
})
}
}
}
Note:
You have to determine if you need to clear the tempResults. If so you need to implement the logic for it at the appropriate place.
To store the tempResults you can make use of technique called memoization. The above described one is a simple one.
Since your results are memozied, this might be an advantage for you if the axios api call results are not gonna be changing for the same input. You might not have to hit the api again for the same query.
The data you store in your tempResults should be the response from the api call or something from which you can determine the arguments that you need to pass to callback.
I think, you might need to debounce the forceRenderEditormethod to avoid repeated update if there are many api being called for each render.
Finally, i could not find a place where draft uses or suggests for an async callback. You might have to check with the library's team if they support/need such a feature. (If needed make the changes and raise a PR if their team is ok with one.)
Update
To bind you can move the functions within the component and write it the below manner.
linkStrategy = (contentBlock, callback, contentState) => {
this.findLinkInText(LINK_REGEX, contentBlock, callback)
}
findLinkInText = (...args) => {
}
And in your constructor you can call it like this
const compositeDecorator = new CompositeDecorator([
.... {
strategy: this.linkStrategy,
component: decorateComponentWithProps(linkComp, {
passit
})
}
....
]);
}
Or if you want to reuse the function across multiple components then you can bind it in the below manner. But make sure to use the same state in all of the sharing components(or use callback to define custom states)
Your constructor will be like
const compositeDecorator = new CompositeDecorator([
.... {
strategy: linkStrategy.bind(this),
component: decorateComponentWithProps(linkComp, {
passit
})
}
....
]);
}
Your link strategy will be like this
function linkStrategy(contentBlock, callback, contentState) {
findLinkInText.call(this,LINK_REGEX, contentBlock, callback);
}
You can use either of the above method to bind your functions.

Assuming everything else is working, I would expect something more what is below. I am not set up to use Axios so I can't actually test this.
// I made these global variables because you are trying to use them
// in both the .post and the .then
var start; // global variable
var URL // global variable
function processResponse (aStart, aStartURL, passit) {
// do something with the reponse data
}
function findLinkInText(regex, contentBlock) {
const text = contentBlock.getText();
let matchArr;
if((matchArr = regex.exec(text)) !== null){
start = matchArr.index;
// renamed because it is not the url, its data passed to the server
URL = matchArr[0];
console.log(URL);
axios.post('/url',{url:URL}).then(response => {
passit = response.data;
processResponse (start, start + URL.length, passit)
}).catch(function (error) {
console.log(error);
});
}
}

Note that "axios depends on a native ES6 Promise implementation to be supported. If your environment doesn't support ES6 Promises, you can polyfill." which means that this does not work in all browser. ( I could not get it to work in IE 11 even after include the recommended include here https://github.com/stefanpenner/es6-promise
Here is my code that I did get working in Edge:
axios(
{
method: 'post',
url: '/wsService.asmx/GetDTDataSerializedList',
data: { parameters: 'one' },
callback: function () { alert() }
})
.then(response =>{
response.config.callback();
console.log(response);
})
.catch(function (error) {
console.log(error);
});
});
With that I changed your code accordingly as shown below
function findLinkInText(regex, contentBlock, callback) {
const text = contentBlock.getText();
let matchArr, start;
if ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index;
var URL = matchArr[0];
console.log(URL);
// I am putting the parameters used to make the call in a JSON object to be used in the "then"
var myparameters = { myCallback: callback, URL: URL, start: start };
axios({
method:post,
url:'/url',
data: { url: URL },
// note that i am attaching the callback to be carrired through
myParameters: myparameters
}).then(response => {
// This section is the callback generated by the post request.
// it cannot see anything unless they declared globally or attached to the request and passed through
// which is what i did.
passit = response.data
var s = response.config.myParameters.start;
var u = response.config.myParameters.URL
response.config.myParameters.myCallback(s, s + u.length)
})
}
}

Related

How can I set global variables in chrome extension

I work on a chrome extension which has a const user that is set in the first lines of the background.js, when I change its values and use them later in a different function they always return to their set values in the beginning.
I read online about hoisting and that it is to source of the problem but how can I avoid it so i can actually manipulate the variables and use them later on with no problem.
I also saw the use of globalThis but when i tried to call or set the values in the functions with globalThis it didn't work as well.
Here is my first lines of background.js:
var user={
connection:undefined,
id:"id",
username:"username",
room:["id","password","url","tabid","ishost"],
room_members:[],
in_room:false,
room_process:[false,"intervelId"],
current_tab:["url","id"]
}
I also tried working with globalThis when setting some of the values of user like showen here:
function connect(){
globalThis.user.connection = new WebSocket('ws://192.168.3.16:8765');
globalThis.user.connection.onopen = function(e) {
globalThis.user.connection.send("get_id");
};
globalThis.user.connection.onmessage=function(event){
let data=event.data.split(',');
let temp_i=data[0]
globalThis.user.id=temp_i;
let temp_u=data[1]
globalThis.user.username=temp_u;
console.log("connection: ",user.id,",",user.username);
};
}
and for example if I got from the last function that user.id="Oak" when I try to recall the value in a different function like this:
function test(){
console.log(user.id);
}
it shows "id" and not "Oak"
EDIT
I tried to use the storage.local by putting the var user in the onInstalled event of the program and then by having two functions I can get the current value of user out of the local storage and then update it as well.
here is my code:
first init of the var user:
chrome.runtime.onInstalled.addListener(async () => {
//opens welcome page
let url = chrome.runtime.getURL("htmls/hello.html");
let tab = await chrome.tabs.create({ url });
//setting up the user var in chrome's local storage
chrome.storage.local.get(['userLocal'], async function (result) {
var user={
connection:undefined,
id:"id",
username:"username",
room:["id","password","url","tabid","ishost"],
room_members:[],
in_room:false,
room_process:[false,"intervelId"],
current_tab:["url","id"]
}
chrome.storage.local.set({userLocal: user}, function () {}); // save it in local.
});
//connect to server and recive id
connect()
});
the two functions function:
function get_user(){
chrome.storage.local.get(['userLocal'], async function (result) {
var userLocal = result.userLocal;
return userLocal;
});
}
function update_user(tmp_user){
chrome.storage.local.get(['userLocal'], async function (result) {
var userLocal = tmp_user;
chrome.storage.local.set({userLocal: userLocal}, function () {}); // save it in local.
return userLocal;
});
}
and for example if I use what I've written:
function connect(){
var user=get_user();
user.connection = new WebSocket('ws://192.168.3.16:8765'); //'ws://192.168.3.16:8765'
user=update_user(user);
}
I get this error: "Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'connection')"
The problem is that a background service worker is loaded when it is needed, and unloaded when it goes idle. So, after a few seconds of being idle, the background script unloads itself, making you lose the changes you made to the user object.
You could save the user object in chrome.storage.local, and then read and modify from there. Something like this:
// background.js; "initial lines" =>
chrome.storage.local.get(['userLocal'], async function (result) {
let userLocal = result.userLocal
if (userLocal === undefined) {
// it means there was nothing before. This way you don't overwrite
// the user object every time the backgorund.js loads.
var user={
connection:undefined,
id:"id",
username:"username",
room:["id","password","url","tabid","ishost"],
room_members:[],
in_room:false,
room_process:[false,"intervelId"],
current_tab:["url","id"]
}
chrome.storage.local.set({userLocal: user}, function () {}); // save it in local.
}})
When you want to modify the user, you retrieve it from local and modify the result before saving it back in the localStorage.
// example of modifying a property =>
chrome.storage.local.get(['userLocal'], async function (result) {
let userLocal = result.userLocal
if (userLocal !== undefined) {
userLocal.connection = new WebSocket('ws://192.168.3.16:8765');
// ...
chrome.storage.local.set({userLocal: userLocal}, function () {}); // save it in local.
}})
You can also just read the user properties, without modifying them and saving them back to the localStorage.
############ EDIT:
because chrome.storage.local is asynchronous, try to transform you code like this:
async function get_user(){
return new Promise(async function (res, rej) {
chrome.storage.local.get(['userLocal'], async function (result) {
var userLocal = result.userLocal;
res(userLocal);
});
});
}
var user= await get_user(); // call it like this.

Promise resolves earlier than expected and not returning an AXIOS call value

I'm making a request to a service that generates several strings in rapid succession. My problem arise from having to return a promise with this service, as I do not control the service process that returns the string.
I want to emphasize the fact that I necessarily need to return a promise.
Right now, what I have is the main function (handler.ts) and it doesn't mantain any business logic, including only the following:
public static callback: any;
public static async init(configuration, callback): Promise<any> {
this.callback = callback;
...
return new Promise((resolve, reject) => {
try {
const result = await Service.bootstrap(configuration)
return resolve(result)
} catch(err) {
reject(err)
}
}
}
This handler calls the service, which has the bootstrap function that performs a call to a .js file that obtains some information from my computer and then returns a string about it.
import loadComputerInformation from 'load.js'
public async bootstrap() : Promise<any> {
loadComputerInformation();
function useComputerInfoString() {
// window.getInfo is generated by loadComputerInformation(), and I can not control
// when does it return it, as it generates several strings with rapid succession
// and until the function exists, I will not get my string.
if (typeof window.getInfo !== 'function') {return;}
const data = window.getInfo();
if (data.finished) {
clearTimeout(timeoutId);
const infoString = data.computerInfo;
Service.axiosCall(infoString);
}
}
// I have to set an interval to perform the call several times, and until it resolves it
// it will not stop performing this call.
const timeoutId = setInterval(useComputerInfoString, 500);
}
return;
}
Therefore, the problem that I'm facing is that my promise gets lost in another thread, and I can not return the value from Service.axiosCall(infoString), which is just a standard axios call, but that necessarily needs the infoString.
Adding the axios call function just in case it is useful. Right now, I'm using the callback passed to the handler.js to return the axios call, but I want to be able to include it in the Promise without the necessity of a callback function
public static async axiosCall(blackbox): Promise<any> {
await axios.post('https://somecall.com/info', blackbox)
.then((response) => { this.callback(element)
return element);
}
}
Any idea of how to solve this?
Highlight
Please note that loadComputerInformation() asynchronously loads Window.getInfo(), but it does not resolve only one value, but several, therefore I can not await on Window.getInfo() because at the beggining it does not exist, and will only return undefined
Also, right now, the code is up and running, but the way it is returning the value is with the callback and not as a promise.
Try a bootstrap function whose returned promise resolves with the response returned from an axios call.
This suggestion (based on information in the post) is vanilla JavaScript to demonstrate how to the problem might be approached. Obviously modification to integrate it into the application will be needed:
const bootstrap = () => new Promise( resolve => {
loadComputerInformation();
let state = "load";
const timer = setInterval( ()=> {
if( state == "load") {
if( typeof window.getData != "function") {
return;
}
state = "get";
}
let data;
if( state == "get") {
data = window.getData();
if(!data.finished) {
return;
}
// state = "request";
}
clearInterval(timer);
resolve( axios('post', "https:example.com/info", data.computerInfo) );
}, 100); // 1/10th second?
};
Note this answer has been modified to use a state machine to wait for getData to be loaded asynchronously, then to wait for a call to getData to return a data object with a finished property, before resolving the promise returned with the promise returned by the axios call - the first answer was simpler right up until it needed to wait for getData code to be loaded asynchronously.
Beneath the question lies a problem that may have to be written off to code debt: JavaScript is event driven. loadComputerInformation appears to have been written for its results to be polled. It is hard to imagine a justifiable reason why it was left in that state.

Leaving jQuery, wrote a simple ajax function, but chained methods will not wait

Update: Added a simpler demonstration jsfiddle, https://jsfiddle.net/47sfj3Lv/3/.
reproducing the problem in much less code I'm trying to move away from jQuery.
Some of my code, for populating some tables, has code like this
var hb = new hbLister(url: '#attributes.listURL#')
.getData(#url.page#, #url.per#)
.search(searchColumn, searchParam)
.render();
hbLister would initialize some things
getData would perform an $.ajax call
search wouldconsole.log('filtering data') and apply the search conditions against a javascript object
render would put the results on the page.
Importantly, search wouldn't fire until after the ajax call in getData finished.
So, now I have this ajax constructor. I've abbreviated as much of this code as I can.
let ajax = function (options, hooks, headers) {
let that = this;
// enforce parameter types
// copy parameters to this.base
this.base = { options: options, hooks: hooks, headers: headers }
return function (url, options, data, hooks, headers) {
// enforce variable types
// merge options and hooks with their base.counterparts
headers = new Headers(Object.assign({}, that.base.headers, headers));
options.headers = headers;
return fetch(url, options)
.then(response => {
return response.json().then(json => {
console.log('processing');
if (response.ok) {
// it's omitted here but the Base functions are defined
// in the constructor parameters
hooks.successBase(json, response.status, response.headers);
hooks.success(response.json, response.status, response.headers)
} else {
hooks.failureBase(json, response.status, response.headers);
hooks.failure(response.json, response.status, response.headers)
}
})
});
}
}
The idea is that I can say
let tableLister = new ajax()
And thengetData can call
tableLister = tableLister(hb.url, // url
{ type: "GET" }, // options
config.data, // data
{ success: successFn } // hooks, success being the callback
)
The jQuery call would properly give me and then processing and then filtering data.
This function gives me filtering data, an error, and thenprocessing, because I cannot seem to get the chain(.search(...).render()) to wait til the ajax call is done.
Here's a self-contained example on jsFiddle, https://jsfiddle.net/47sfj3Lv/3/
I am sure the answer is in async and await, but I have not been able to get it to work.
Here is an example of what I've tried
return await (async function () {
console.log('fetching')
let fetcher = await fetch(url, options);
console.log('getting json');
return await fetcher.json().then((json) => {
console.log('have json, doing hooks');
if (fetcher.ok) {
let argXHR = { json: json}
hooks.successBase(argXHR, hooks.params);
hooks.success.forEach(v => v(argXHR, hooks.params));
hooks.afterSend(argXHR, hooks.params);
} else {
let argXHR = { json: json,}
hooks.failureBase(argXHR, hooks.params);
hooks.failure.forEach(v => v(argXHR, hooks.params));
hooks.afterError(argXHR, hooks.params);
}
console.log('finished hooks')
})
}())
And no matter what I do, the chain, continues before this await finishes..
I got code with XMLHttpRequest to work. The method chain (.getData().search(...).render()) works with this, because this doesn't allow the ajax function to return before the request is finished and callbacks are executed. **I'd still prefer to make .fetch() work.
let xhr = new XMLHttpRequest()
let urlParams = [];
Object.keys(data).forEach((v) => urlParams.push(v + '=' + encodeURIComponent(data[v])));
urlParams = urlParams.join('&')
xhr.open(options.method, options.url, false);
xhr.onreadystatechange = function(state) {
if (this.readyState == 4) {
let json = JSON.parse(xhr.responseText)
hooks.successBase(json, xhr.status, xhr.getResponseHeader);
hooks.success.forEach(v => v(json, xhr.status, xhr.getResponseHeader));
}
}
xhr.onerror = function() {
let json = JSON.parse(xhr.responseText)
hooks.failureBase(json, xhr.status, xhr.getResponseHeader);
hooks.failure.forEach(v => v(json, xhr.status, xhr.getResponseHeader));
}
for (h in headers) {
xhr.setRequestHeader(h, headers[h])
}
xhr.send(urlParams)
This was difficult for me to understand, so I wanted to share if anyone else has the same issue.
It seems that an async method will break a method chain, there's no way around that. And since fetch is asynchronous, await must be used, and in order for await to be used, the calling method must be declared async. Thus the method chain will be broken.
The way the method chain is called must be changed.
In my OP, I linked https://jsfiddle.net/47sfj3Lv/3/ as a much simpler version of the same problem. StackOverflow's 'fiddle' effectively blocks 'fetch' for security reasons, so I need to use JSFiddle for demonstration.
Here's a working version of the same code using then and how/why it works, and a slightly shorter version, because await can be specified with the the fetch, obviously.
let obj = {};
// methods with fetch ideally should be specified async.
// async calls will automatically return a promise
obj.start = async () => {
console.log('start begins')
let retText = "",
fetcher = fetch('/', {}).then(response => response.text())
.then(text => {
console.log('fetch\'s last then')
retText = text
})
// fetcher has just been declared. It hasn't done anything yet.
console.log('fetch requested, returned length:', retText.length)
// this makes the fetcher and sequential then's happen
await fetcher;
console.log('await let fetch finish, returned length:', retText.length)
// Async functions will return a promise, but the return specified here
// will be passed to the next 'then'
return obj
}
obj.middle = () => {
console.log('middle called')
// Because this is not declared async, it behaves normally
return obj;
}
obj.end = () => {
console.log('end called')
// Because this is not declared async, it behaves normally
}
console.log('Method 1: ', obj.start()
// Because start is Async, it returns obj wrapped in a promise.
// As with any function, step may be named Anything you like
.then((step) => step.middle())
// Middle is not Async, but since it's wrapped in then
// it returns a promise
.then((step) => step.end())
)
// This is just wrapped in a timer so the two logs don't intermix with each other
// This is definitely the preferred method. Non-async chain-methods that return
// a reference to their object, do not need to wrapped in then().
setTimeout(function() {
console.log('------------------------')
console.log('Method 2: ', obj.start()
// Because start is Async, it returns obj wrapped in a promise.
// As with any function, step may be named Anything you like
.then((step) => step.middle().end())
)
}, 3000)

Wait until async is completed (await)

I am trying to get my head around with async/await
I am using https://github.com/sunnylqm/react-native-storage for my project in react-native.
I am using async storage to store few critical information such as selected user locale, I need to retrieve this value before screen is rendered to display screen based on selected locale.
I have tried implementing several helper functions, it works with callback, what I need is to return the value instead of callback and wait until the value is fetched. Below are few examples I tried.
// 1
_selectedLocale = async () => {
try {
const value = await global.storage.load({key: 'selectedLocale'});
return value
} catch (error) {
console.log(value)
}
}
var selectedLocale = _selectedLocale();
// 2
export async function _selectedLocale() {
return storage.load({key: 'selectedLocale'});
}
var selectedLocale = _selectedLocale();
// 3
export function selectedLocale(callback) {
storage.load({key: 'selectedLocale'}).catch(e => {
callback(RNLanguages.language);
}).then(function(locale) {
callback(locale);
});
}
This is not working, I am looking to
Implement a helper function to retrieve a value based on key
Wait until the value is retrieved (sync)
Can someone point me to right direction
Thanks
UPDATE1:
Here is how I am using the callback
selectedLocale(function(locale) {
global.layoutIsRTL = 'ar' == locale;
ReactNative.I18nManager.allowRTL(true);
global.i18n.locale = locale
});
UPDATE2:
It seems someone has already done this, here is the reference https://github.com/sunnylqm/react-native-storage/issues/206 unfortunately I am having hard time understanding it.
async implicitly returns a promise, so you need to await the function call as well if you want the value there:
var selectedLocale = await _selectedLocale();
but you can only do that inside of another async function

How can I keep a firebase function open when it's in another file

I have a file called db.js from which I make all my firebase calls.
I am calling a function in db.js from another file called home.js
How do I make it that the firebase connection stays open and the data gets passed back to home.js? I can't use a promise because that closes the connection.
Here is the function from db.js:
export function getShopNames() {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").on('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
return stores
})
}
and I call it from home like this:
let stores = db.getShopNames()
I want it to work so if a new store gets added to the real-time database, the variable updates
There is no concept of file based scope in JavaScript. The listener will stay active from the moment you call on('value', until you either call off on that same location or until you load a new page.
But your return stores doesn't do anything meaningful right now. It returns a value from the callback function that nobody will ever see/use.
Data is loaded from Firebase asynchronously, which means you can't return it from a function in the normal way. By the time the return statement runs, the data hasn't loaded yet. That's why you'll usually return a so-called promise, which then resolves when the data has loaded.
In your function that'd be:
export function getShopNames() {
return new Promise(function(resolve, reject) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
resolve(stores);
}, (error) => {
reject(error);
})
}
Now you can call this function like this:
getShopNames().then((shopnames) => {
console.log(shopnames);
})
Update: you commented that you also want to handle updates to the shop names, you can't use once() and can't use promises (since those only resolve once).
Instead pass in a custom callback, and invoke that every time the shop names change:
export function getShopNames(callback) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
callback(stores);
})
}
And then call it like:
getShopnames(function(shopnames) {
console.log(shopnames);
});

Categories

Resources