I'm new in javascript/typescript I write blackjack app using typescript and in one player game I have bot as second player.
His script looks like this:
const botTurn = (bot : Player) =>{
while(bot.Points < 21)
{
if(bot.Points < 18)
{
hitMe();
}
else
{
var random = Math.random();
if(random < 0.5)
{
hitMe();
}
else{
break;
}
}
}
stay();
}
and hitMe looks like this:
const hitMe = () =>
{
fetch('https://deckofcardsapi.com/api/deck/' + deckId + '/draw/?count=1')
.then(response => response.json())
.then(data => {
deckLenght = data.remaining;
for(let card of data.cards)
{
var newCard = getCardData(card);
players[currentPlayer].Hand.push(newCard);
renderCard(newCard, currentPlayer);
updatePoints();
updateDeckLenght();
check();
}
});
}
So botTurn doesn't wait for hitMe to finish and my browser hangs
How to fix that?
you can use Async/await it will be better for your case and easier
const hitMe = async () =>
{
let response = await fetch('https://deckofcardsapi.com/api/deck/' + deckId + '/draw/?count=1');
let data = await response.json();
deckLenght = data.remaining;
for(let card of data.cards)
{
var newCard = getCardData(card);
players[currentPlayer].Hand.push(newCard);
renderCard(newCard, currentPlayer);
updatePoints();
updateDeckLenght();
check();
}
});
}
you cannot expect a synchronous-based scoped value by wrapping an asynchronous operation with a synchronous operation.
although you can try to finalize your logic inside resolver, ie: then(result)
Related
What am I doing wrong? I want to get data from all pages in api. After adding the while it stopped working, but I don't know how to start the page loop differently!
getCustomers: function() {
let url = '/crm/customer/';
return axios.get(url).then((response) => {
this.customers = response.data.results;
if (this.customers.length === 100) {
let i = 2;
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
while (this.c.length === 100) {
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
},
What I would do is, first, use an async function called getPageOfResults:
async function getPageOfResults(page) {
const response = await axios.get('/crm/customer/?page=' + page);
return response.data.results; // .results.values()? I don't know
}
Then, inside another async function, you can have a loop that does what you want:
async function getAllResults() {
const customers = [];
let lastResultsLength = 100;
let page = 1;
while (lastResultsLength === 100) {
const newResults = await getPageOfResults(page);
page++;
lastResultsLength = newResults.length;
customers = customers.concat(newResults);
}
return customers;
}
So you've got a variable keeping track of what page your on, and you keep on getting new pages until you get a page with less than 100 results.
You're adding all the results together with the concat function, and returning the entire list at the end.
client.on('message', async function(message) {
if(message.author.bot) return;
if(message.content.includes('!reps')) {
let usname = message.mentions.users.first().username;
let usId = message.mentions.users.first().id;
message.channel.send('Calculating ' + usname + `'s reps`);
let repsChannel = client.channels.cache.get('66518878191720806');
let sbChannel = client.channels.cache.get('66503449499402200');
let chData = await repsChannel.messages.fetch( {limit:100}).catch(err => console.log(err));
let mentamt = chData.filter(chData => chData.mentions.members.has(usId))
let amts = mentamt.size
console.log(amts)
This portion gives me the first 100 lines to check through and it seems to work, however, after including the While loop to check through the next 100 lines, I cant make it return the total sum
while(chData.size === 100) {
let lastKeyId = chData.lastKey();
chData = await repsChannel.messages.fetch( {limit:100, before: lastKeyId }).catch(err => console.log(err));
let tvalue = chData.filter(chData => chData.mentions.members.has(usId)
let tamt = 0
}
sbChannel.send('> '+ usname +' has '+ amts);
}
});
I want to auto increment the value of tamt whenever a member has been mentioned in a message.
The main purpose of this is to calculate the total number of times a member has been mentioned.
Using async-await in for loop, you can achieve it easily.
const delay = () =>
new Promise((r) => {
setTimeout(r, 300, new Date());
});
(async function () {
async function page(count) {
const result = await delay();
// Do your work here
return `Page ${count}:: ${result}`;
}
const chunkSize = 100;
const pageCount = Math.floor(1121/chunkSize) + 1
for (let pageNum = 0; pageNum < pageCount; pageNum++) {
const data = await page(pageNum);
console.log(data)
}
})();
I suspect I've fundementally misunderstood Javascript promises, any ideas?
I have a pretty function that queries a database containing music that looks like this:
function searchDatabaseForTrack(query,loadedResults){
loadedResults = loadedResults || [];
desiredResults = 100;
if (loadedResults.length < desiredResults) {
try {
databaseApi.searchTracks(query, {"offset":loadedResults.length, "limit":"50", }).then(function(data){
i=0
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
for (thing in data.tracks.items){
loadedResults.push(data.tracks.items[i]);
i=i+1;
}
console.log(loadedResults.length, " tracks collected");
searchDatabaseForTrack(query,loadedResults)
}
});
} catch(err) {
console.log("ERROR!", err)
console.log(loadedResults)
return loadedResults;
}
} else {
console.log(loadedResults)
return loadedResults;
}
}
And then a bit later, I try to call and use the data retrieved.
function getArtistTracks(artistName){
searchDatabaseForTrack(artistName).then(function(data){
console.log(songs);
songs.sort(function(a,b){
var c = new Date(a.track.album.release_date);
var d = new Date(b.track.album.release_date);
return d-c;
});
console.log("songs", songs);
var newsongs=[];
i=0
for (song in songs) {
newsongs.push(songs[i].track.uri);
i++
};
return newsongs;
});
}
What I'm trying to do is get the second function "getArtistTracks" to wait for the completion of the query in the first function. Now I could just call the databaseApi.searchTracks directly, but there's a limit of 50 tracks returned per result — which kind of screws me over.
searchDatabaseForTrack().then(...) shouldn't work since searchDatabaseForTrack() doesn't return a promise, so you can either return a promise or use an async function.
instead of a recursive function, you could simply call databaseApi in a for loop,
the desiredResult should be an argument and not hardcoded in the function,
async function searchDatabaseForTrack(query, desiredResults){
let loadedResults = [], data, currOffset = 0;
const iterations = Math.ceil(desiredResults / 50);
for(let n = 0 ; n < iterations; n++){
cuurOffset = n * 50;
data = await databaseApi.searchTracks(query, {"offset":currOffset, "limit":"50", });
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
loadedResults = loadedResults.concat(data.tracks.items);
console.log(loadedResults.length, " tracks collected");
}
}
return loadedResults;
}
the rest should be fine as long as you add .catch() to handle errors ( as mentionned in previous answer ) which are thrown automatically without the need of the try/catch block :
function getArtistTracks(artistName, 100){
searchDatabaseForTrack(artistName).then((songs) => {
// your previous code
})
.catch((err) => {
// handle error
});
});
Have searchDatabaseForTrack use Promise.all to return the loadedResults after all results have been gotten. Also, make sure not to implicitly create global variables as you're doing with thing. For example, try something like this:
async function searchDatabaseForTrack(query) {
const desiredResults = 100;
const trackPromises = Array.from(
({ length: Math.ceil(desiredResults / 50) }),
(_, i) => {
const offset = i * 50;
return databaseApi.searchTracks(query, { offset, limit: 50 });
}
);
const itemChunks = await Promise.all(trackPromises);
const loadedResults = itemChunks.reduce((a, { tracks: { items }}) => (
[...a, ...items]
), []);
return loadedResults;
};
and
searchDatabaseForTrack(artistName).then((loadedResults) => {
// do stuff with loadedResults
})
.catch((err) => {
console.log("ERROR!", err)
// handle error
});
I am trying to write a GUI frontend that uses a service to get data about the system. I am using a net.Socket for the client end of this. I want to be able to access certain variables assigned in the data event handlers in other modules but the assignment does not stay after that callback function finishes.
Problematic code:
client.on('data', (data) => {
var array = [...data];
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
module.exports.hasBattery = lastBatteryJSON.hasBattery == 'true';
module.exports.isCharging = lastBatteryJSON.isCharging == 'true';
module.exports.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
Those three exported variable assignments don't actually work, the variables always either stay undefined or their default values outside of the function. I tried using a Promise to solve this problem but got the same result. I'm at a loss and I can't find any other questions or forum posts that solve this problem.
EDIT
I do not have the option of moving the code that depends on those variables into the callback. In order to do that I would have to wait for the data every frame and flood the server as a result.
As apple commented; you can export an object and mutate it every time you receive data:
const data = {};
client.on('data', (data) => {
var array = [...data];
array.splice(0, 2);
for (var i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
//mutate the data object
data.hasBattery = lastBatteryJSON.hasBattery == 'true';
data.isCharging = lastBatteryJSON.isCharging == 'true';
data.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
//export the data object
module.exports.batteryData = data;
Or as CertainPerformance answered you can have the caller decide when to ask for the information and provide a promise.
Here is an extended version of CertainPerformance answer that listens to error as well so a promise can be rejected and cleans up the event listeners when promise is resolved or rejected:
//wrapper for client.on to add and remove event listeners
const listeners = (function(){
var listenerCounter = -1;
const listeners = [];
const triggerEvent = event => data =>{
listeners.filter(
listener=>listener[2] === event
).forEach(
listener=>listener[1](data)
);
};
client.on('data', triggerEvent("data"));
client.on('error', triggerEvent("error"));//assuming you have an error event
return {
add:(event,fn)=>{
listenerCounter = listenerCounter + 1;
if(listenerCounter>1000000){
listenerCounter=0;
}
listeners.push([listenerCounter,fn,event]);
return listenerCounter;
},
remove:num=>{
listeners = listeners.filter(
listener=>{
num !== listener[0];
}
)
}
}
}());
//convert data to object or false
const getObjectFromData = data => {
var array = [...data];
var dataInBuffer="";
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
return {
hasBattery : lastBatteryJSON.hasBattery == 'true',
isCharging : lastBatteryJSON.isCharging == 'true',
lastBatteryReading : parseFloat(lastBatteryJSON.batteryLife)
};
}
return false;
}
//export this function
const getBatteryData = () =>
new Promise((resolve,reject) => {
const removeListeners = ()=>{
listeners.remove(okId);
listeners.remove(errorId);
}
const okId = listeners.add(
"data",
data=>{
const resultObject = getObjectFromData(data);
if(resultObject){
resolve(data);
removeListeners();//clean up listeners
}else{
//not sure of on data is triggered multiple times by client.on.data
// if it is then at what point do we need to reject the returned promise?
}
}
)
const errorId = listeners.add(
"error",
error=>{
reject(error);
removeListeners();//clean up listeners
}
)
});
//you can call getBatteryData like so:
//getBatteryData()
// .then(batteryData=>console.log(batteryData))
// .catch(error=>console.warn("an error getting battery data:",error))
Your module should export a function that returns a promise that returns the desired values. Also, use const and not var when possible:
let resolveObj;
const haveData = new Promise((resolve) => {
let resolved = false;
client.on('data', (data) => {
const array = [...data];
array.splice(0, 2);
for (let i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
const {
hasBattery,
isCharging,
batteryLife,
} = JSON.parse(dataInBuffer.split(';')[1]);
resolveObj = {
hasBattery: hasBattery === 'true',
isCharging: isCharging === 'true',
lastBatteryReading: Number(batteryLife),
};
if (!resolved) resolve();
resolved = true;
}
dataInBuffer = '';
});
});
const getData = () => haveData.then(() => resolveObj);
module.exports = getData;
Then consume with
moduleFunction().then(({ hasBattery, isCharging, lastBatteryReading }) => {
// do something with results
});
If called before resolveObj is populated, the promise will wait until the first client.on('data' to resolve. After that, the function will return a promise that resolves immediately to the current value of resolveObj (which will be properly updated on client.on('data')
I am trying to pull the data from realms and then process it using the native call. Once I get the processed data I further filter it in react native code and then give the return data. But till the time I complete all the activity the return parameter is already executed. Please have a look at the below code snippet:
async getArray() {
realm = await this.getRealMObject();
favoriteArray = realm.objects(constants.SCHEMA_SAVE);
return favoriteArray;
}
async getLevel() {
let low = 'false';
let favoriteArray = await this.getAllArray();
let getDetails = [];
if (favoriteArray.length > 0) {
for (var y in favoriteArray) {
NativeModules.Get.GetAsyncTask(
brd[0].toUpperCase(),
strChk, '1',
kResponse => {
if (Platform.OS == 'ios') {
getDetails = kResponse;
} else {
getDetails = JSON.parse(kResponse);
}
for (let x = 0; x < getDetails.length; x++) {
const strLevel = this.getPercentage(getDetails[x]);
if (strLevel <= 20) {
low = true;
}
}
});
}
} else {
low = false;
}
return low;
}
Above is the code snippet. So when I call getlevel() before the processing is finished I get the return low value.
How can I handle, this? I want the data to be processed and then get the return value.