I’m making Javascript below.
The entire system consists of HTML, Javascript and Python. they send/receive/show some data.
when to press generateBtn(), Form data on HTML is sent to Javascript and Python.
Javascript waits data processing of Python ( waitUpdate() ). After that, Javascript shows the data python sent ( generateProfile() ).
So far, waitUpdate() doesn’t work.
when to start “const waitUpdate = async function(user_uid) {”, it doesn’t get into “db.collection(“users”)…where(“generate_flag”, “==”, true).onSnapshot((querySnapshot) => {”. And it shows console.log('Flag : ’ + flag); (this shows Flag: true) right before sleep(1000);. So it gets into infinity loop shows “Flag: true”.
When to confirm generate_flag in CloudFirestore account, it shows true. So I thought
it’s got into “.onSnapshot((querySnapshot) => {”. But actually It’s not, somehow I mentioned above.
Did anyone realize how to solve this issue?
I’d appreciate it, if you would tell me the way to solve it.
Thank you.
function generateBtn() {
var user_uid = firebase.auth().currentUser.uid
//console.log("In createCloze() : user_uid : " + user_uid);
const waitUpdate = async function(user_uid) {
let flag
flag = true
while(flag){
//console.log("while() in waitUpdate() : user_uid = " + user_uid); //Okay.
db.collection("users").doc(user_uid).collection("logdata").where("generate_flag", "==", true)
.onSnapshot((querySnapshot) => {
console.log("In .onSnapshot((querySnapshot) =>");//It's never been through here so far.
querySnapshot.forEach((doc) => {
console.log("In querySnapshot.forEach((doc) =>");
if(doc.data().mypythonfeedback_flag == false){
flag = false;
//console.log('Flag : ' + flag + ' in if(doc.data().mypythonfeedback_flag == false)');
}
}); //querySnapshot.forEach((doc) => {
}); //.onSnapshot((querySnapshot) => {
console.log('Flag : ' + flag);
sleep(1000);
} //while(flag){
}
//Omitted. This shows the data.
const generateProfile = async function(user_uid) {
console.log('In generateProfile()');
} //const generateProfile = async function(user_uid) {
const processAll = async function(user_uid) {
await waitUpdate(user_uid)
await generateProfile(user_uid)
}
processAll(user_uid)
}
Data is loaded from Firebase asynchronously, because it may need to come from the network. While the data is being loaded, your other code continues to execute. And then when the data is loaded, your callback is called with that data.
What this means in practice is that your console.log('Flag : ' + flag) runs before the flag = false is ever executed, so it always shows the default value. You can most easily verify this by setting breakpoints on these lines and running in a debugger, or by checking that Flag : true shows before In querySnapshot.forEach((doc) => in your output.
The simplest solution for this is to put the code that needs the flag into the callback that is called when the data is loaded, so like this:
db.collection("users").doc(user_uid).collection("logdata").where("generate_flag", "==", true)
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
if(doc.data().mypythonfeedback_flag == false){
flag = false;
}
}); //querySnapshot.forEach((doc) => {
console.log('Flag : ' + flag); // 👈
});
To allow the caller to await until the flag becomes false, you can return a custom promise:
const waitUpdate = async function(user_uid) {
return new Promise((resolve, reject) => {
let flag
flag = true
db.collection("users").doc(user_uid).collection("logdata").where("generate_flag", "==", true)
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
if(doc.data().mypythonfeedback_flag == false){
flag = false;
resolve();
}
});
});
})
}
As you'll see, we've removed the loop here - and instead call resolve() (which then triggers the await) from within the callback that listens for updates from the database.
Related
So, I have an adaptation of https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms working in vanilla JS that I am attempting to adapt to use React. Instead of every user being a broadcaster, in my version, only the room creator is the broadcaster.
I've hit an issue. In the React version, when a viewer navigates to the room, they are not receiving the stream! I have no idea why since they use the same RoomClient class: https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms/blob/master/public/RoomClient.js
This line const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, }); seems to be causing the problem, since the log following it doesn't get printed. Inside the consume function, my log that says 'hi' is executed, but 'blah' is not.
Here is a screenshot of the client console:
The most important functions are found below. For the entire class, please click the github link above.
async consume(producer_id) {
//let info = await roomInfo()
console.log('consume ', producer_id);
console.log('dddddddddddd', await this.getConsumeStream(producer_id));
this.getConsumeStream(producer_id).then(
function ({ consumer, stream, kind }) {
console.log('blah');
this.consumers.set(consumer.id, consumer);
let elem;
console.log('clg kind === ', kind);
if (kind === 'video') {
console.log('cons vid');
elem = document.createElement('video');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
elem.className = 'vid';
this.remoteVideoEl.appendChild(elem);
} else {
elem = document.createElement('audio');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
this.remoteAudioEl.appendChild(elem);
}
consumer.on(
'trackended',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
consumer.on(
'transportclose',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
}.bind(this)
);
}
async getConsumeStream(producerId) {
const { rtpCapabilities } = this.device;
console.log('rtpcaps ', rtpCapabilities);
const data = await this.socketRequest('consume', {
rtpCapabilities,
consumerTransportId: this.consumerTransport.id, // might be
producerId,
}).then((data) => {
console.log('daaatttaaa', data);
return data;
});
const { id, kind, rtpParameters } = data;
console.log('data === ', data);
let codecOptions = {};
console.log('aaaaaaaaaaaaaa', this.consumerTransport.consume);
const consumer = await this.consumerTransport
.consume({
id,
producerId,
kind,
rtpParameters,
codecOptions,
})
.then((result) => {
console.log('bbbbbbb', result);
return result;
});
console.log('consumer === ', consumer);
const stream = new MediaStream();
console.log('stream === ', stream);
stream.addTrack(consumer.track);
console.log('kind ', kind);
return {
consumer,
stream,
kind,
};
}
Many thanks for your time
---update---
This line never resolves: const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, })
It ends up executing some complex-looking functions from packages. In fact, It seems to get stuck in an infinite loop in the package called sdp-transform, after executing a few lines in mediasoup-client.
I don't even understand how chrome debugger's working. Because if I put a breakpoint on that line where ...consume(... is called, and click 'step', it parses this line: let answer = await this._pc.createAnswer() ... Which is part of a function called receive, which wasn't called by the breakpoint line... Please, somebody help.
You said something interesting there. 'hi' gets logged and 'blah' not. There isn't much code in between them. This should make us wonder how it is even possible.
Maybe create the same just without any distractions around.
Promise.resolve()
.then(console.log('hi'))
.then(function() {
console.log('blah');
})
What would that do? and why?
.then is a function that expects to be given a function (to be called when the promise resolves...)
What does console.log('hi') return? And when is it executed?
You did not want to execute the log when creating the promise chain and you also want to maybe return the result again since you want to work with the value.
So my guess would be. Changing it to:
.then(result => {
console.log('hi')
return result
})
brings you at least a step closer.
For a specific function, a list of tags (rfids) are required from a database, but various tables should be checked.
The issue is centered around the problem of a long running operation isn't awaited upon to finish before proceeding to the next.
Problem:
The code called below runs as follows (# numbers correspond to number in code below):
getAllTags() is called, starting the proc
await db.dbRfid.findAll() returns with X amount of valid results (not Promise objects)
start filtering call on valid objects & await finish (await place since some function calls inside are async calls.
call const driverTags = await tag.getDrivers();
At this point, one would expect this function to return the result and proceed to the next function i.e.
// #5
const truckTags = await tag.getTrucks();
What infact happens is for each of the allTags items, the getDrivers() gets called and the filter() exits and the code continues on by executing this next:
// #6
if (pureTags) {
return filteredTags;
}
Question:
To my understanding, I am awaiting async operations correctly, yet it seems like the filter only accepts/allows one async operation.
I assume I did something wrong, yet I am unable to the cause of the problem. Any advice would be appreciated!
Full code implementation below:
Called with e.g.
const listOfTags = await getAllTags();
Specific tag requirements (all valid, unused, enabled & unrevoked tags)
const listOfTags = await getAllTags(false, true, true, true);
Problem code:
// #1
const getAllTags = async (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
// #2
let allTags = await db.dbRfid.findAll()
// #3
let filteredTags = await allTags.filter(async tag => {
// check tag used
if (unused) {
// #4
const driverTags = await tag.getDrivers();
// #5
const truckTags = await tag.getTrucks();
const userTags = await tag.getUsers();
if (driverTags.length > 0) {
return false;
}
if (truckTags.length > 0) {
return false;
}
if (userTags.length > 0) {
return false;
}
}
// check tag enabled
if (enabled && !tag.enabled) {
return false;
}
// check tag revoked or return true
return notRevoked && !tag.revoked;
});
// return tags as is
// #6
if (pureTags) {
return filteredTags;
}
return filteredTags.map(tag => {
return {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}
});
}
Update
I should mention:
no 'errors' of any sort are shown to indicate of some problem.
the .getDrivers() is a getter created by sequelize which returns a promise.
Update 2
After comment by evgeni fotia
I originally had this code, however chose not to include it as it may havve complicated matters. However, this it the original code I attempted with the addition of the await Promise.all().
const getAllTags = async (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
let allTags = await db.dbRfid.findAll()
let filteredTags = await Promise.all(
// await allTags.filter(async tag => { //tried the await here too - for good measure
allTags.filter(async tag => {
// check tag used
if (unused) {
const driverTags = await tag.getDrivers();
const truckTags = await tag.getTrucks();
const userTags = await tag.getUsers();
if (driverTags.length > 0) {
return false;
}
if (truckTags.length > 0) {
return false;
}
if (userTags.length > 0) {
return false;
}
}
// check tag enabled
if (enabled && !tag.enabled) {
return false;
}
// check tag revoked or return true
return notRevoked && !tag.revoked;
})
);
// return tags as is
if (pureTags) {
return filteredTags;
}
return filteredTags.map(tag => {
return {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}
});
}
Please note, after running the code in this update or in the original question, I see the debugger hitting the getDrivers() method, then the (HTTP) response is sent to the client, and only after 0.5~1s I see the getDrivers() method returning and proceeding to the next method.
Your central problem is, as T.J. Chowder has commented, that you are trying to .filter() on something that is sometimes a boolean, and sometimes a promise.
You should do the mapping and the filtering in different steps. I tend to prefer the .then() syntax, so here's my approach:
const getAllTags = (pureTags = false, unused = true, enabled = true, notRevoked = false) => {
return db.dbRfid.findAll()
.then(allTags => allTags.map(tag => Promise.resolve(
enabled === tag.enabled && notRevoked === !tag.revoked && Promise.all([
tag.getDrivers(), tag.getTrucks(), tag.getUsers()
]).then(results => results.some(r => r.length))
).then(ok => ok ? tag : null)))
.then(pendingTags => Promise.all(pendingTags))
.then(resolvedTags => resolvedTags.filter(tag => tag))
.then(filteredTags => filteredTags.map(tag => pureTags ? tag : {
id: tag.id,
rfid: tag.rfid,
expiryDate: tag.expiryDate,
revoked: tag.revoked,
enabled: tag.enabled
}));
};
The logic of this code:
fetch tags from DB
produces Promise<Tag[]>, i.e. a promise for an array of tags
map each tag to a new promise that resolves either to the tag itself, or null, depending on a condition we figure out using Promise.resolve(), to account for the potentially(*) async nature of that check
produces Promise<Promise<Tag|null>[]>
wait for all those inner promises to resolve using Promise.all()
produces <Promise<(Tag|null)[]>
filter out the null values (i.e. tags we don't want)
produces <Promise<Tag[]>
map the tags to the overall result data structure we want, depending on pureTags
produces <Promise<(Tag|object)[]>
(*) The Promise.all() is only invoked if the preconditions check out, due to short circuiting. If not, it's simply a Promise.resolve(false), which resolves to false immediately. In the other case it's going to be Promise.resolve(<Promise<Boolean>), which works exactly the same way as Promise.resolve(Boolean). This way we can unify the synchronous and the asynchronous test.
In the subsequent .then() we can decide whether to return the tag - or null, to indicate that this tag failed the filter conditions. The null values are then picked out by resolvedTags.filter(tag => tag).
Note that your code serializes the three async checks (getDrivers, getTrucks, getUsers) because each is awaited before the next is started. Using Promise.all() lets them run in parallel, which is one of the reasons why I dislike using async/await for everything.
I leave rewriting this into async/await style as an exercise.
This perplexes me. I'm six months into a firebase project and have been using Javascript for firebase-functions. I've learned a lot along the way by adding transactions, promises, batch writes and neat tricks. However, it seems like complete luck for a function to execute correctly. More often than not, the functions do execute correctly, but there are strange periods when bursts of consecutive function calls where functions half complete with no errors in the logs.
For example. I have a function for when a new user joins my app. It does a little bit of server data construction and also notifies the two admins that a new user has joined. Last night I did a test run with two new users and got no notification, but their user profiles constructed correctly on the server database. I checked the function logs and there were no errors.
Am I not handling Promises in the correct way? If a firebase function hangs, does it mess up the next few function calls?
exports.onNewUser = functions.firestore
.document('/users/{userId}')
.onCreate(async (snapshot, context) => {
user = snapshot.data().username;
//Notification payload
const payload = {
notification: {
title: `New user!`,
body: `${user} has joined [AppName]`
}
};
var promises = [];
//Check if usename unique
var passed = true;
promises.push(db.runTransaction(async t => {
const docRef = db.collection('users').doc('index');
const doc = await t.get(docRef);
var newIndex = doc.data().usernames;
if (newIndex[user.toUpperCase()] == true) {
t.delete(snapshot.ref);
passed = false;
return null;
} else {
newIndex[user.toUpperCase()] = true;
t.set(docRef, { 'usernames': newIndex });
}
}));
if (!passed) return Promise.all(promises);
//add new user to Algolia database
const algoliasearch = require('algoliasearch');
const algoliaClient = algoliasearch(functions.config().algolia.appid, functions.config().algolia.apikey);
const collectionIndex = algoliaClient.initIndex(collectionIndexName);
await saveDocumentInAlgolia(snapshot, collectionIndex);
//Notify Admins
db.collection('notificationTokens')
.doc(admin1)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
db.collection('notificationTokens')
.doc(admin2)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
return Promise.all(promises);
});
Just change
return Promise.all(promises);
to
return await Promise.all(promises);
You have to wait till the promises resolve before you return the function, as that would stop the instance of the cloud function.
I am working on a script that will fetch posts from a paginated REST API provided by a website managed by WordPress.
What I realized was that WordPress REST API is paginated and got a cap of 100 objects per request.
Within the code below, I am trying to fetch posts page by page and with an amount of 20 posts per page. In the end, I am joining all the fetched objects into one big object.
My problem is, that the fetch fails with HTTP 404 response, since the last request contain less than 20 posts.
I would like to adjust the variable named 'limitPerPage', if the fetch returns with a 404 and decrement the variable until i get a 200 HTTP response.
My challenge is, that I not experienced working with fetch promise.
Please see my current script below:
console.log('REST API is this: ' + apiUrl);
const getPosts = async function(pageNo = 1) {
let limitPerPage = 20;
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
let apiResults = await fetch(requestUrl)
.then(function(response){
return response.json();
})
.catch(function(error){
console.log(error.status);
});
return apiResults;
}
const getEntirePostList = async function(pageNo = 1) {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
if (results.length > 0) {
return results.concat(await getEntirePostList(pageNo+1));
} else {
return results;
}
};( async () => {
const entireList = await getEntirePostList();
console.log(entireList);
})
();
I expect the code to decrement the variable 'limitPerPage' by 1, if the fetch returns a 404 HTTP response.
I do not necessary ask for a final solution to my problem. I would appreciate a suggestion to another way for me to structure my code, to get the result I need.
Thanks!
I think the following code should work. Always use try...catch block inside async functions
let entireList = [];
let finishEvent = new Event('finished');
document.addEventListener('finished', function (e) {
console.log(entireList);
}, false);
const getPosts = function (pageNo = 1) {
let limitPerPage = 20;
let requestUrl = `${apiUrl}?page=${pageNo}&per_page=${limitPerPage}`;
return fetch(requestUrl)
.then(function (response) {
return response.json();
})
.catch(function (error) {
console.log(error.status);
return false;
});
}
const getEntirePostList = async function (pageNo = 1) {
try {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
if (results && (results.length > 0)) {
entireList.concat(results);
getEntirePostList(pageNo + 1);
} else {
document.dispatchEvent(finishEvent);
}
return;
} catch(e) {
console.log(e)
}
};
getEntirePostList();
I managed to solve the issue myself with the help from the suggestions above. Please find the solution here:
// Constant variable with the assigned value of a joined string containing the base URL structure of the REST API request.
const apiUrl = websiteUrl + '/wp-json/wp/v2/punkt/';
// Logging out the base URL of the REST API.
console.log('REST API is this: ' + apiUrl);
// Local variable with the default value of true assigned. Variable is used to control the paginated fetch of the API.
let keepfetchingPosts = true;
// Local variable that contains the limit of posts per request.
let limitPerPage = 20;
// Constant variable, which is assigned a function as the value. The function is async and will return a promise.
// The function takes one argument. The argument holds the value of the page number.
const getPosts = async function(pageNo = 1) {
// Local variable assigned with the base URL string of the REST API request. Additional queries are added to the end of the string.
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
// Logging out the REST API request
console.log('URL is this: ' + requestUrl);
// Logging out the argument 'pageNo'
console.log('Retreiving data from API for page : ' + pageNo);
// Local variable assigned with a fetch function that returns a promise. The URL request are used as the function argument.
let apiResults = await fetch(requestUrl)
// If request is success, then log and return the following to the local variable.
.then(function(response){
// Logging out the status code of the response.
console.log('HTTP response is: ' + response.status);
// return JSON and status code of the XHR request
return {
data: response.json(),
status: response.status
}
})
// Catch the error and log it out within the console.
.catch(function(error){
console.log('HTTP response is: ' + error.status)
});
// If the length of the request is less than the limitPerPage variable and status code is 200, then...
if (apiResults.length < limitPerPage && apiResults.status === 200){
// Set the boolean to false
keepfetchingPosts = false;
// Return the JSON of the successfull response.
return apiResults.data;
} else if (apiResults.status === 200) {
// If the status code is 200, then return the JSON of the successfull response
return apiResults.data;
} else {
// Otherwise, set the boolean to false
keepfetchingPosts = false;
}
}
// Defining a constant variable that holds and async fynction. An async functon will always return a promise.
// The function takes one argument, which is set to 1 by default.
const getEntirePostList = async function(pageNo = 1) {
// Try and catch statement used to handle the errors that might occur.
try {
// Constant variable which is set to the return variable of the function getPost(). Get post returns the successfull paginated response of the request.
const results = await getPosts(pageNo);
// Logging out a string including the length of the array.
console.log('Current array contain ' + results.length + ' items...');
// Conditional statement that checks if the length of the array named 'results' is less than the variable named limitPerPage. Condition is also checked, if bolean is true.
// If the conditions are met, the code will join the arrays into one big array.
if (results.length < limitPerPage && keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine that last array to the existing array.
console.log('Combining last array!');
// Return the combined array.
return results;
} else if (keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine the recent fetched array to the existing array.
console.log('Combining arrays!');
// Returning the new combined array and increments the pageNo variable with 1.
return results.concat(await getEntirePostList(pageNo+1));
} else {
// Logging out a string that indicates the script will stop fetching more posts from the REST API.
console.log('Stop fetching posts and return results');
// Returning the complete array.
return results;
}
// Catch statement that takes the argument of the error that occured.
} catch(error) {
// Logging out the error.
console.log(error);
}
};( async () => {
// Constant variable with the assigned value received from the function
const entireList = await getEntirePostList();
// Logging out the enite list of results collected from the REST API
console.log(entireList);
})
();
The code above returns a complete array of the JSON response from all paginated REST API calls.
You could use a while loop, and decrement limitPerPage if the status code isn't 200:
console.log('REST API is this: ' + apiUrl);
const getPosts = async pageNo => {
let limitPerPage = 20;
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
let res = { status: 0 }
while (res.status !== 200) {
res = await fetch(requestUrl)
.then(r => ({ data: r.json(), status: r.status }))
.catch(({status}) => console.log(status))
limitPerPage--
}
return res.data
}
const getEntirePostList = async (pageNo = 1) => {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
return results.length > 0
? results.concat(await getEntirePostList(pageNo + 1))
: results;
}
(async () => {
const entireList = await getEntirePostList();
console.log(entireList);
})()
I'm trying to use async/await in my firebase function but I am getting an error. I have marked the function as async but when I try to use await inside of it I get error: Expression has type void. Put it on its own line as a statement.
I think this is strange because I think it each await function call is already in its own line as a statement. So, I'm not sure what to do. Any help would be appreciated.
For clarity, I am making a request with the Cheerio web scrape library, and then trying to make two async function calls during each loop with the .each method.
export const helloWorld = functions.https.onRequest((req, response) => {
const options = {
uri: 'https://www.cbssports.com/nba/scoreboard/',
transform: function (body) {
return cheerio.load(body);
}
};
request(options)
.then(($) => {
$('.live-update').each((i, element) => {
const homeTeamAbbr = $(element).find('tbody').children('tr').eq(0).find('a').html().split("alt/").pop().split('.svg')[0];
const awayTeamAbbr = $(element).find('tbody').children('tr').eq(1).find('a').html().split("alt/").pop().split('.svg')[0];
const homeTeam = $(element).find('tbody').children('tr').eq(0).find('a.team').text().trim();
const awayTeam = $(element).find('tbody').children('tr').eq(1).find('a.team').text().trim();
let homeTeamStatsURL = $(element).find('tbody').children('tr').eq(0).find('td').html();
let awayTeamStatsURL = $(element).find('tbody').children('tr').eq(1).find('td').html();
const gameTime = $(element).find('.pregame-date').text().trim();
homeTeamStatsURL = homeTeamStatsURL.match(/href="([^"]*)/)[1] + "roster";
awayTeamStatsURL = awayTeamStatsURL.match(/href="([^"]*)/)[1] + "roster";
const matchupString = awayTeamAbbr + "#" + homeTeamAbbr;
const URLString = "NBA_" + urlDate + "_" + matchupString;
// var docRef = database.collection('NBASchedule').doc("UpcommingSchedule");
// var boxScoreURL = "www.cbssports.com/nba/gametracker/boxscore/" + URLString;
// var setAda = docRef.set({[URLString]:{
// homeTeam: homeTeam,
// awayTeam: awayTeam,
// date: gameTime,
// homeTeamAbbr: homeTeamAbbr,
// awayTeamAbbr: awayTeamAbbr,
// homeTeamStatsURL: homeTeamStatsURL,
// awayTeamStatsURL: awayTeamStatsURL,
// boxScoreURL: boxScoreURL
// }}, { merge: true });
getTeamPlayers(homeTeamStatsURL, matchupString);
getTeamPlayers(awayTeamStatsURL, matchupString);
console.log("retrieved schedule for "+ matchupString + " on " + urlDate)
});
response.send("retrieved schedule");
})
.catch(function (err) {
console.log("error " + err);
});
});
The function I am calling just makes another request and then I'm trying to log some data.
function getTeamPlayers(playerStatsURL, matchupString) {
const options = {
uri: playerStatsURL,
transform: function (body) {
return cheerio.load(body);
}
};
console.log(playerStatsURL + " stats url");
request(options)
.then(($) => {
console.log('inside cheerio')
$('tbody').children('tr').each(function(i, element){
const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();
const injury = $(element).children('td').eq(1).children('span').eq(1).children('.icon-moon-injury').text().trim();
const news = $(element).children('td').eq(1).children('span').eq(1).children('.icon-moon-news').text().trim();
const playerUrl = $(element).children('td').eq(1).children('span').eq(1).find('a').attr('href');
const playerLogsUrl = "https://www.cbssports.com" + playerUrl.replace('playerpage', 'player/gamelogs/2018');
console.log(playerName + ": Inj: " + injury + " News: " + news);
// database.collection('NBAPlayers').add({[playerName]:{
// '01 playerName': playerName,
// '03 playerLogsUrl': playerLogsUrl,
// '04 inj': injury,
// '05 news': news
// }})
// .then(docRef => {
// console.log("ID " + docRef.id);
// //getPlayerLogs(playerLogsUrl, playerName, docRef.id);
// })
// .catch(error => console.error("Error adding document: ", error));
});
});
}
async/await is supported in the Node version 8 (which is deployable as Cloud Functions for Firebase). Specifically, use 8.6.1 (at the time of this writing).
As for awaiting x2 inside a loop - I don't think this is best practice.
Instead, push all these requests into an array, then Promise.all so as to fetch all in parallel.
just in case someone comes after. As Ron stated before, you shouldn't use async/await inside a traditional for loop if the second call doesn't need the value of the first, as you will increase the run time of the code.
Furthermore you can't use async/await inside of a forEach=> or a map=> loop, as it won't stop and wait for the promise to resolve.
The most efficient way is to use Promise.all([]). Here I leave a great youtube video of a crack that explains async/await and Promises => https://www.youtube.com/watch?v=vn3tm0quoqE&t=6s
As to one of the questions in the comments by DarkHorse:
But all I want is for getTeamPlayers() to execute and write to the database so I don't know why I need to return anything.
In Firebase Functions all functions need to return something before the final response.
For example in this case, he has created an http function. Before he ends the function with response.send("retrieved schedule"); you need to finish each and every function triggered. As Firebase Functions will clean up after the final response, erasing and stoping anything that it is still running. So any function that hasn't finish will be killed before doing its job.
Returning a promise is the way Firebase Functions knows when all executions have finished and can clean up.
Hop it helps :)