Следующий код генерирует отчет, показывающий производительность членов клана в игре под названием Clash Royale. Все довольно понятно, мы работаем с большим количеством https-запросов, массивов и сортировок.
const https = require("https");
const clanTag = "L2P8GRJR";
const token = "TOKEN_HERE"
let rawData="";
let topDonators = [];
let worstDonators = [];
let bestPlayers = [];
let counter = 0;
const mergeByProperty = (target, source, prop) => {
source.forEach(sourceElement => {
let targetElement = target.find(targetElement => {
return sourceElement[prop] === targetElement[prop];
})
targetElement ? Object.assign(targetElement, sourceElement) : null;
})
}
const finalise = () => {
if (counter !== 2) return;
let result = `CR Clan Youtube Weekly Report
°•°•°•°•°•°•°•°•°•°•°•°•
— Top Donators ——
~~~~~~~~~~~~~~~~~~
${topDonators.map((x, i) => `${i + 1}. ${x.name} - ${x.donations}`).join('n')}
--------------------
— Back Donators ( < 100 ) ——
~~~~~~~~~~~~~~~~~~~
${worstDonators.map((x, i) => `${50 - i}. ${x.name} - ${x.donations}`).join('n')}
--------------------
— Net Best Players ——
(Donations × Fame earned ÷ (100 × 1000))
===================
${bestPlayers.map((x, i) => `${i + 1}. ${x.name} - ${x.netScore}`).join('n')}
===================
{ ${bestPlayers[0].name} } is the player of the week, congrats!
Data automatically fetched and compiled at ${new Date().toUTCString()}.
Earn fame in river race and donate cards to improve your net score which will be published on Sunday. Player of The Week will be chosen based on net score. Better net score may also result in promotion.
CR may not show full report to some players, hence this report is also published to the Discord server (link in the description).`
console.log(result)
}
https.get(`https://proxy.royaleapi.dev/v1/clans/%23${clanTag}/members`, {
headers: {
authorization: token
}
}, (res) => {
res.on("data", (data) => {
rawData += data
})
res.on("end", () => {
rawData = JSON.parse(rawData).items
topDonators = [...rawData].sort((a, b) => b.donations - a.donations).slice(0, 10)
rawData.forEach(elm => {
if (elm.donations < 100) worstDonators.push(elm)
})
worstDonators.sort((a, b) => a.donations - b.donations)
counter++;
finalise()
})
})
https.get(`https://proxy.royaleapi.dev/v1/clans/%23${clanTag}/currentriverrace`, {
headers: {
authorization: token
}
}, res => {
let warData = [];
res.on("data", data => {
warData += data
})
res.on("end", () => {
warData = JSON.parse(warData).clan.participants;
mergeByProperty(rawData, warData, "tag")
bestPlayers = [...rawData].map(elm => {elm.netScore = elm.fame * elm.donations / 100000; return elm;}).sort((a,b) => b.netScore - a.netScore).slice(0, 10)
counter++;
finalise()
})
})
Прокси-сервер используется, но знайте, что это необходимо.
Производительность является основным моментом здесь, но я хотел бы получить обзор по любому пункту.
1 ответ
Классный проект!
Хорошая работа.
Чистый код важен, и это одна из вещей, с которой я борюсь. Много.
Из вашего кода я вижу, что и вам это принесет большую пользу.
const token = "TOKEN_HERE";
Токены и конфиденциальная информация, которую вы должны попытаться сохранить в .env
файлы. Особенно, если ваш код может быть доступен на github или подобных сайтах.
const mergeByProperty...
Эта функция кажется слишком сложной для того, что вы хотите сделать. Я правда не думаю, что тебе это вообще нужно.
То же самое для этого:
const finalise...
Попробуйте извлечь логику в функции, особенно если вам нужно использовать ее в других местах. Попытайтесь идентифицировать повторяющийся код, если вы это видите, тогда вы сможете создать для него функцию.
Не повторяйся. Код, который вы повторяете, сложно поддерживать, потому что, если вы сделаете небольшое изменение, вам нужно будет внести это изменение повсюду.
Имена ваших функций и переменных одинаково важны, и это сложнее, чем кажется.
Я бы поступил иначе (и даже это можно значительно улучшить):
const baseUrl="https://proxy.royaleapi.dev/v1/clans";
const clanUrl = `${baseUrl}/#${clanTag}/`;
const requestOptions = {
headers: {
authorization: token
}
}
// wrap your requests into a promise
const GET = (url, options = {}) => {
return new Promise((resolve, reject) => {
const request = https.get(url, options, (response) => {
if (response.statusCode < 200 || response.statusCode > 299) {
reject(new Error(response.statusCode));
}
let data="";
response.on('data', (chunk) => data += chunk);
response.on('end', () => resolve(JSON.parse(data)));
});
request.on('error', (err) => reject(err))
})
};
// do all the data manipulation inside these helper functions
// this transforms the data you get from the server into the format
// you want
const getMembers = (members) => {
const donors = {
top: [...members].sort((a, b) => b.donations - a.donations).slice(0, 10),
worst: [...members].filter(x => x.donations < 100).sort((a, b) => a.donations - b.donations)
}
return donors;
}
// transform the data from server into the format you want
const getCurrentRiverRace= (clanParticipants) => {
const clan = {
participants: clanParticipants,
bestPlayers: [...clanParticipants].map(x => {
return {
...x,
netScore: x.fame * x.donations / 100000
};
}).sort((a,b) => b.netScore - a.netScore).slice(0, 10)
}
return clan;
}
// this one makes both requests to the server at the same time
// Notice the async. Since your requests return promises, this is how you
// handle the promises
const requests = async () => {
const membersCall = GET(`${clanUrl}/members`, requestOptions);
const riverRaceCall = GET(`${clanUrl}/currentriverrace`, requestOptions);
const [members, riverRace] = await Promise.all([membersCall, riverRaceCall]);
return {
members: getMembers(members),
riverRace: getCurrentRiverRace(riverRace)
}
}
// here you just 'finalise' or output whatever you want
// you should handle the errors so maybe wrap it in a try/catch.
const output = async () => {
try {
//
const r = await requests();
const stats = `
CR Clan Youtube Weekly Report
°•°•°•°•°•°•°•°•°•°•°•°•
— Top Donators ——
${stats.members.top}
....
`;
console.log(stats);
return stats;
} catch (err) {
// here you handle the error in case the promise rejects
// (returns error)
// console.log is NOT error handling but it gives you an idea
console.log(err);
}
}
Попробуйте разгадать это вместе. Я не уверен, что он на 100% функционален, но должно быть довольно близко. Считайте это домашним заданием 🙂
Все, что вы сделали с моим кодом, замечательно! Хотя у меня есть одно небольшое возражение / вопрос. Вы заметили, что
finalise()
слишком сложно. Я не знаю, что в этом можно улучшить. Я думаю, чтоcounter
Система может быть улучшена, вероятно, с помощью обещаний, но я не уверен.— ммаизмма
Извините, у меня не было времени углубиться в детали. Но здесь есть еще кое-что, что нужно улучшить. Вам действительно не нужен счетчик, и да, вы можете обещать свои запросы.
— иросгрим
Я дам вам более полный ответ, как только смогу
— иросгрим
я только что обновил фрагмент кода. Удачи!
— иросгрим
Большое спасибо, что нашли время ответить мне. Мне понравилось, что вы также добавили части кода обработки ошибок. Логика обещаний определенно на высшем уровне. Еще раз большое спасибо!
— ммаизмма