Good evening! I have a problem.
I am creating a collection of NFTs using this js file.
What I am trying to modify is that once it reads the images in the layers folder, creates the final image and writes the metadata, these read and used images are deleted in the source folder (${basePath}/layers).
const basePath = process.cwd();
const { NETWORK } = require(`${basePath}/constants/network.js`);
const fs = require("fs");
const sha1 = require(`${basePath}/node_modules/sha1`);
const { createCanvas, loadImage } = require(`${basePath}/node_modules/canvas`);
const buildDir = `${basePath}/build`;
const layersDir = `${basePath}/layers`;
const {
format,
baseUri,
description,
background,
uniqueDnaTorrance,
layerConfigurations,
rarityDelimiter,
shuffleLayerConfigurations,
debugLogs,
extraMetadata,
text,
namePrefix,
network,
solanaMetadata,
gif,
} = require(`${basePath}/src/config.js`);
const canvas = createCanvas(format.width, format.height);
const ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = format.smoothing;
var metadataList = [];
var attributesList = [];
var dnaList = new Set();
const DNA_DELIMITER = "-";
const HashlipsGiffer = require(`${basePath}/modules/HashlipsGiffer.js`);
let hashlipsGiffer = null;
const buildSetup = () => {
if (fs.existsSync(buildDir)) {
fs.rmdirSync(buildDir, { recursive: true });
}
fs.mkdirSync(buildDir);
fs.mkdirSync(`${buildDir}/json`);
fs.mkdirSync(`${buildDir}/images`);
if (gif.export) {
fs.mkdirSync(`${buildDir}/gifs`);
}
};
const getRarityWeight = (_str) => {
let nameWithoutExtension = _str.slice(0, -4);
var nameWithoutWeight = Number(
nameWithoutExtension.split(rarityDelimiter).pop()
);
if (isNaN(nameWithoutWeight)) {
nameWithoutWeight = 1;
}
return nameWithoutWeight;
};
const cleanDna = (_str) => {
const withoutOptions = removeQueryStrings(_str);
var dna = Number(withoutOptions.split(":").shift());
return dna;
};
const cleanName = (_str) => {
let nameWithoutExtension = _str.slice(0, -4);
var nameWithoutWeight = nameWithoutExtension.split(rarityDelimiter).shift();
return nameWithoutWeight;
};
const getElements = (path) => {
return fs
.readdirSync(path)
.filter((item) => !/(^|\/)\.[^\/\.]/g.test(item))
.map((i, index) => {
return {
id: index,
name: cleanName(i),
filename: i,
path: `${path}${i}`,
weight: getRarityWeight(i),
};
});
};
const layersSetup = (layersOrder) => {
const layers = layersOrder.map((layerObj, index) => ({
id: index,
elements: getElements(`${layersDir}/${layerObj.name}/`),
name:
layerObj.options?.["displayName"] != undefined
? layerObj.options?.["displayName"]
: layerObj.name,
blend:
layerObj.options?.["blend"] != undefined
? layerObj.options?.["blend"]
: "source-over",
opacity:
layerObj.options?.["opacity"] != undefined
? layerObj.options?.["opacity"]
: 1,
bypassDNA:
layerObj.options?.["bypassDNA"] !== undefined
? layerObj.options?.["bypassDNA"]
: false,
}));
return layers;
};
const saveImage = (_editionCount) => {
fs.writeFileSync(
`${buildDir}/images/${_editionCount}.png`,
canvas.toBuffer("image/png")
);
};
const genColor = () => {
let hue = Math.floor(Math.random() * 360);
let pastel = `hsl(${hue}, 100%, ${background.brightness})`;
return pastel;
};
const drawBackground = () => {
ctx.fillStyle = background.static ? background.default : genColor();
ctx.fillRect(0, 0, format.width, format.height);
};
const addMetadata = (_dna, _edition) => {
let dateTime = Date.now();
let tempMetadata = {
name: `${namePrefix} #${_edition}`,
description: description,
image: `${baseUri}/${_edition}.png`,
dna: sha1(_dna),
edition: _edition,
date: dateTime,
...extraMetadata,
attributes: attributesList,
compiler: "CryptoGioconda Engine",
};
if (network == NETWORK.sol) {
tempMetadata = {
//Added metadata for solana
name: tempMetadata.name,
symbol: solanaMetadata.symbol,
description: tempMetadata.description,
//Added metadata for solana
seller_fee_basis_points: solanaMetadata.seller_fee_basis_points,
image: `image.png`,
//Added metadata for solana
external_url: solanaMetadata.external_url,
edition: _edition,
...extraMetadata,
attributes: tempMetadata.attributes,
properties: {
files: [
{
uri: "image.png",
type: "image/png",
},
],
category: "image",
creators: solanaMetadata.creators,
},
};
}
metadataList.push(tempMetadata);
attributesList = [];
};
const addAttributes = (_element) => {
let selectedElement = _element.layer.selectedElement;
attributesList.push({
trait_type: _element.layer.name,
value: selectedElement.name,
});
};
const loadLayerImg = async (_layer) => {
return new Promise(async (resolve) => {
const image = await loadImage(`${_layer.selectedElement.path}`);
resolve({ layer: _layer, loadedImage: image });
});
};
const addText = (_sig, x, y, size) => {
ctx.fillStyle = text.color;
ctx.font = `${text.weight} ${size}pt ${text.family}`;
ctx.textBaseline = text.baseline;
ctx.textAlign = text.align;
ctx.fillText(_sig, x, y);
};
const drawElement = (_renderObject, _index, _layersLen) => {
ctx.globalAlpha = _renderObject.layer.opacity;
ctx.globalCompositeOperation = _renderObject.layer.blend;
text.only
? addText(
`${_renderObject.layer.name}${text.spacer}${_renderObject.layer.selectedElement.name}`,
text.xGap,
text.yGap * (_index + 1),
text.size
)
: ctx.drawImage(
_renderObject.loadedImage,
0,
0,
format.width,
format.height
);
addAttributes(_renderObject);
};
const constructLayerToDna = (_dna = "", _layers = []) => {
let mappedDnaToLayers = _layers.map((layer, index) => {
let selectedElement = layer.elements.find(
(e) => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index])
);
return {
name: layer.name,
blend: layer.blend,
opacity: layer.opacity,
selectedElement: selectedElement,
};
});
return mappedDnaToLayers;
};
/**
* In some cases a DNA string may contain optional query parameters for options
* such as bypassing the DNA isUnique check, this function filters out those
* items without modifying the stored DNA.
*
* #param {String} _dna New DNA string
* #returns new DNA string with any items that should be filtered, removed.
*/
const filterDNAOptions = (_dna) => {
const dnaItems = _dna.split(DNA_DELIMITER);
const filteredDNA = dnaItems.filter((element) => {
const query = /(\?.*$)/;
const querystring = query.exec(element);
if (!querystring) {
return true;
}
const options = querystring[1].split("&").reduce((r, setting) => {
const keyPairs = setting.split("=");
return { ...r, [keyPairs[0]]: keyPairs[1] };
}, []);
return options.bypassDNA;
});
return filteredDNA.join(DNA_DELIMITER);
};
/**
* Cleaning function for DNA strings. When DNA strings include an option, it
* is added to the filename with a ?setting=value query string. It needs to be
* removed to properly access the file name before Drawing.
*
* #param {String} _dna The entire newDNA string
* #returns Cleaned DNA string without querystring parameters.
*/
const removeQueryStrings = (_dna) => {
const query = /(\?.*$)/;
return _dna.replace(query, "");
};
const isDnaUnique = (_DnaList = new Set(), _dna = "") => {
const _filteredDNA = filterDNAOptions(_dna);
return !_DnaList.has(_filteredDNA);
};
const createDna = (_layers) => {
let randNum = [];
_layers.forEach((layer) => {
var totalWeight = 0;
layer.elements.forEach((element) => {
totalWeight += element.weight;
});
// number between 0 - totalWeight
let random = Math.floor(Math.random() * totalWeight);
for (var i = 0; i < layer.elements.length; i++) {
// subtract the current weight from the random weight until we reach a sub zero value.
random -= layer.elements[i].weight;
if (random < 0) {
return randNum.push(
`${layer.elements[i].id}:${layer.elements[i].filename}${
layer.bypassDNA ? "?bypassDNA=true" : ""
}`
);
}
}
});
return randNum.join(DNA_DELIMITER);
};
const writeMetaData = (_data) => {
fs.writeFileSync(`${buildDir}/json/_metadata.json`, _data);
};
const saveMetaDataSingleFile = (_editionCount) => {
let metadata = metadataList.find((meta) => meta.edition == _editionCount);
debugLogs
? console.log(
`Writing metadata for ${_editionCount}: ${JSON.stringify(metadata)}`
)
: null;
fs.writeFileSync(
`${buildDir}/json/${_editionCount}.json`,
JSON.stringify(metadata, null, 2)
);
};
function shuffle(array) {
let currentIndex = array.length,
randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}
return array;
}
const startCreating = async () => {
let layerConfigIndex = 0;
let editionCount = 1;
let failedCount = 0;
let abstractedIndexes = [];
for (
let i = network == NETWORK.sol ? 0 : 1;
i <= layerConfigurations[layerConfigurations.length - 1].growEditionSizeTo;
i++
) {
abstractedIndexes.push(i);
}
if (shuffleLayerConfigurations) {
abstractedIndexes = shuffle(abstractedIndexes);
}
debugLogs
? console.log("Editions left to create: ", abstractedIndexes)
: null;
while (layerConfigIndex < layerConfigurations.length) {
const layers = layersSetup(
layerConfigurations[layerConfigIndex].layersOrder
);
while (
editionCount <= layerConfigurations[layerConfigIndex].growEditionSizeTo
) {
let newDna = createDna(layers);
if (isDnaUnique(dnaList, newDna)) {
let results = constructLayerToDna(newDna, layers);
let loadedElements = [];
results.forEach((layer) => {
loadedElements.push(loadLayerImg(layer));
});
await Promise.all(loadedElements).then((renderObjectArray) => {
debugLogs ? console.log("Clearing canvas") : null;
ctx.clearRect(0, 0, format.width, format.height);
if (gif.export) {
hashlipsGiffer = new HashlipsGiffer(
canvas,
ctx,
`${buildDir}/gifs/${abstractedIndexes[0]}.gif`,
gif.repeat,
gif.quality,
gif.delay
);
hashlipsGiffer.start();
}
if (background.generate) {
drawBackground();
}
renderObjectArray.forEach((renderObject, index) => {
drawElement(
renderObject,
index,
layerConfigurations[layerConfigIndex].layersOrder.length
);
if (gif.export) {
hashlipsGiffer.add();
}
});
if (gif.export) {
hashlipsGiffer.stop();
}
debugLogs
? console.log("Editions left to create: ", abstractedIndexes)
: null;
saveImage(abstractedIndexes[0]);
addMetadata(newDna, abstractedIndexes[0]);
saveMetaDataSingleFile(abstractedIndexes[0]);
console.log(
`Created edition: ${abstractedIndexes[0]}, with DNA: ${sha1(
newDna
)}`
);
});
dnaList.add(filterDNAOptions(newDna));
editionCount++;
abstractedIndexes.shift();
} else {
console.log("DNA exists!");
failedCount++;
if (failedCount >= uniqueDnaTorrance) {
console.log(
`You need more layers or elements to grow your edition to ${layerConfigurations[layerConfigIndex].growEditionSizeTo} artworks!`
);
process.exit();
}
}
}
layerConfigIndex++;
}
writeMetaData(JSON.stringify(metadataList, null, 2));
};
module.exports = { startCreating, buildSetup, getElements };
I tried to use this code by inserting it inside the main document, but it doesn't work.
try {
fs.unlinkSync(path)
//file removed
} catch(err) {
console.error(err)
}
Can anyone tell me how can I solve?
Related
This question already has an answer here:
Weird behavior with Promise throwing "Unhandled promise rejection" error
(1 answer)
Closed 8 months ago.
I'm using Promise.race so I can time out the fetch call. During a test to an API endpoint that doesn't exist, I'm getting an "Unhandled promise rejection" error. The code is supposed to console.log once upon any failure. Where am I not handling the promise rejection? FYI the main function is function Retrieve
window.path = "http://localhost:3000/records";
function isPrimary(color) {
let colorIsPrimary = false;
(color.startsWith("red") || color.startsWith("blue") || color.startsWith("yellow")) ? colorIsPrimary = true : null;
return colorIsPrimary;
}
function transformData(records,page) {
const transformedData = {
"ids" : [],
"open": [],
"closedPrimaryCount": 0,
"previousPage": null,
"nextPage": null
}
let closedPrimaryCount = 0;
records.forEach((record, index) => {
if (index < 10) {
transformedData.ids.push(record.id);
record["isPrimary"] = false;
isPrimary(record.color) ? record.isPrimary = true : null;
record.disposition == "open" ? transformedData.open.push(record) : null;
if (record.disposition == "closed") {
isPrimary(record.color) ? closedPrimaryCount++ : null;
}
}
})
transformedData.closedPrimaryCount = closedPrimaryCount;
let previousPage = null;
page > 1 ? previousPage = page - 1 : null;
transformedData.previousPage = previousPage;
let nextPage = null;
records.length > 10 ? nextPage = page + 1 : null;
transformedData.nextPage = nextPage;
return transformedData;
}
function promiseTimeout(promise, ms) {
let timeout = new Promise((resolve, reject) => {
let timeoutID = setTimeout(() => {
clearTimeout(timeoutID);
reject("fetch failed - timeout");
}, ms)
})
return Promise.race([promise, timeout]);
}
function doFetch(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => {
if (!response.ok) {
reject(new Error("fetch failed - non 200"));
}
response.json()
.then((records) => {
resolve(records);
})
.catch((error) => {
reject(new Error("fetch failed - error from response.json"));
})
})
.catch((error) => {
reject(new Error("fetch failed - error from fetch"));
})
})
}
function retrieve({page = 1, colors = ["red", "brown", "blue", "yellow", "green"], thetest = false, windowPath = window.path} = {}) {
return new Promise((resolve,reject)=>{
!thetest ? windowPath = "http://localhost:3000/records" : null;
// limit set to 11 so we can determine nextPage
const limit = "11";
const offset = "" + ((page * 10) - 10);
let colorArgs = "";
colors.forEach((color, index) => {
colorArgs = colorArgs + "&color[]=" + color;
});
const requestQuery = `limit=${limit}&offset=${offset}${colorArgs}`;
const requestURL = new URI(windowPath);
requestURL.query(requestQuery);
const promiseRace = promiseTimeout(doFetch(requestURL.toString()), 4000);
promiseRace.then((records) => {
const transformedData = transformData(records, page);
resolve(transformedData);
})
promiseRace.catch((error) => {
console.log(error);
})
});
};
export default retrieve;
After ggorlen's excellent advice, I refactored to this much cleaner (and test-passing) code:
async function getTransformedData(url,page) {
try {
const response = await fetch(url);
if (response.ok) {
const records = await response.json();
const transformedData = transformData(records,page);
return(transformedData);
} else {
throw new Error("failed");
}
}
catch(error) {
console.log(error);
}
// getTransformedData
}
function retrieve({page = 1, colors = ["red", "brown", "blue", "yellow", "green"]} = {}) {
// limit set to 11 so we can determine nextPage
const limit = "11";
const offset = "" + ((page * 10) - 10);
let colorArgs = "";
colors.forEach((color, index) => {
colorArgs = colorArgs + "&color[]=" + color;
});
const requestQuery = `limit=${limit}&offset=${offset}${colorArgs}`;
const requestURL = new URI(window.path);
requestURL.query(requestQuery);
return getTransformedData(requestURL.toString(),page);
};
What i am trying to do is to display available rooms which are in data
i map the rooms using data.map , check if one is available between a given ( checkin and checkout date ) using availdata[p.roomId][date].i==0 , if not it will display the available room .
the code works fine but as soon as it finds an available room it stops (which means it stops at the else statement ) and won't look for the rest of the rooms .
Any suggestions ?
using break ; doesn't have anything to do with the problem i am facing .
import React,{useState,useEffect} from 'react';
import HotelCards from './HotelCards';
import styles from '../styles/Options/Options.module.css';
import {Checkkout as checkkout} from "./Hero";
import {Checkkin as checkkin} from "./Hero";
import {format} from "date-fns";
let HC=[];
let prices =[];
let notavailableat="";
let rowss=[];
export default function Options({selectedGuest}) {
const [availdata, setavailData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isLoading2, setIsLoading2] = useState(false);
const [data, setData] = useState([]);
// request Headers
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
};
const requestOptions2 = {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
};
//Get the rooms info along with the ID
const fetchData = () => {
fetch('http://localhost:3001/api/rooms', requestOptions)
.then(response => response.json())
.then((result) =>{
console.log("roooms"+result)
setData(result.rooms)
setIsLoading2(true);
}
)
.catch((err) => console.log("error"));
};
useEffect(() => {
fetchData();
}, []);
//get the i and p variables
function fetchData1() {
fetch('http://localhost:3001/api/availability', requestOptions2)
.then(response => response.json())
.then((result) =>{
setavailData(result.availability[0])
console.log('ooooooooooh'+result.availability[0][7264])
setIsLoading(true);
}
)
.catch((err) => console.log("error"));
}
useEffect(() => {
fetchData1();
}, []);
prices.length=0;
var strToDatein = new Date(checkkin)
var strToDateout = new Date(checkkout)
let trys = 0;
data.map((p) =>{
if (isLoading && isLoading2 ){
for (var day = strToDatein; day < strToDateout; day.setDate(day.getDate() + 1)) {
HC.length=0;
console.log(day + "dekhel for");
var diplaydate = format(day,"dd MMM ");
var date = format(day, 'yyyyMMdd');
if (availdata[p.roomId][date].i==0){
rowss.push(<p key={availdata[p.roomId][date]}> not available at {diplaydate} </p>);
notavailableat="not available at "+diplaydate;
console.log(+p.roomId+"not available at "+diplaydate)
break;
}
else
{console.log("dateeee"+ date);
rowss.length=0;
prices.length=0;
prices.push(availdata[p.roomId][date].p1);
var total_price = 0;
if(prices.length!==0){
for (var i=0;i<=prices.length-1;i++){
total_price+=parseFloat(prices[i]);
}
}
console.log("room:"+p.roomId+"price?"+availdata[p.roomId][date].p1)
HC.push(<div key={p.roomId}>
<HotelCards
idroom={p.roomId}
title={p.roomName.toUpperCase()}
status={true}
price={total_price}
img={p.pictures[0].url}
avail={1111}
rows={rowss}
guest={selectedGuest}
/></div>)
}}
}
})
return (
<div className={`${styles.containers}`}>
{HC}
</div>
);
}
The problem in your code is this assignment in the for loop:
var day = strToDatein;
Dates are objects and objects are not copied. The references are copied. day and strToDatein contain references to the same object. day.setDate(day.getDate() + 1) modifies strToDatein. After the first room was found, strToDatein < strToDateout returns false and the for loop is skipped for all other rooms.
You can fix your problem with a real copy of the Date object in the for loop:
const data = [{
roomId: 0,
roomName: '0'
}, {
roomId: 1,
roomName: '1'
}, {
roomId: 2,
roomName: '2'
}];
const isLoading = true;
const isLoading2 = true;
const strToDatein = new Date(2022, 0, 1);
const strToDateout = new Date(2022, 0, 3);
const HC = [];
const availdata = [{
"20220101": {
i: 0,
p1: 100
}
}, {
"20220101": {
i: 2,
p1: 100
},
"20220102": {
i: 2,
p1: 100
},
"20220103": {
i: 2,
p1: 100
}
}, {
"20220101": {
i: 0,
p1: 100
}
}];
const rowss = [];
const prices = [];
function format(date) {
return `${date.getFullYear()}${`${date.getMonth()+1}`.padStart(2, 0)}${`${date.getDate()}`.padStart(2, 0)}`;
}
data.forEach((p) => {
if (isLoading && isLoading2) {
for (var day = new Date(strToDatein); day < strToDateout; day.setDate(day.getDate() + 1)) {
console.log(day + "dekhel for");
var diplaydate = format(day, "dd MMM ");
var date = format(day, 'yyyyMMdd');
if (availdata[p.roomId][date].i == 0) {
rowss.push(`<p key = {
availdata[p.roomId][date]
}> not available at {
diplaydate
}</p>`);
notavailableat = "not available at " + diplaydate;
console.log(+p.roomId + "not available at " + diplaydate)
break;
} else {
console.log("dateeee" + date);
rowss.length = 0;
prices.length = 0;
prices.push(availdata[p.roomId][date].p1);
var total_price = 0;
if (prices.length !== 0) {
for (var i = 0; i <= prices.length - 1; i++) {
total_price += parseFloat(prices[i]);
}
}
console.log("room:" + p.roomId + "price?" + availdata[p.roomId][date].p1)
HC.push(`<div key={${p.roomId}}>` +
'<HotelCards' +
` idroom={${p.roomId}}` +
` title={${p.roomName.toUpperCase()}}` +
` status={true}` +
` price={${total_price}}` +
' img={p.pictures[0].url}' +
' avail={1111}' +
' rows={rowss}' +
' guest={selectedGuest}' +
'/></div>');
}
}
}
});
console.log(HC);
I am creating a Mongoose seed and creating documents of "pets". For every shelter in my db (a document of 20) I want to insert a random number of the array of pets created in the first lines of my code.
I am looking to get:
Shelter 1: { dogs: ["dog.id1", "dog.id18"] }...Shelter 20: { dogs: ["dog.id14", "dog.id2", "dog.id17"] }
Where the number and id's of those pets are randomized.
I have tried with the code below but: While I don't get any errors, I am only getting the first two shelters with "dogs", the rest appears blank: like this:
Shelter 1: { dogs: ["dog.id1", "dog.id18"] }, Shelter 2: { dogs: ["dog.id2"] }...Shelter 3-20: { dogs: [ ] }
I guess the problem is on the map function and the assignment I am doing of the amounts.
const shelterPromises = shelters
.map((shelter) => {
const petAmount = randomNumber(1, 10);
const randomPets = pets.slice(prevIndex, petAmount);
if (randomPets.length) {
prevIndex = prevIndex + petAmount;
return ShelterModel.findByIdAndUpdate(shelter._id, {
dogs: randomPets
});
}
return null;
})
.filter(Boolean);
await Promise.all(shelterPromises);
console.info('> shelters updated!');
};
my full code below:
const faker = require('faker');
const PetModel = require('../models/Pets');
const ShelterModel = require('../models/Shelter');
const randomNumber = (min, max) =>
Math.floor(Math.random() * (max - min)) + min;
const randomArray = (array) => array[Math.floor(Math.random() * array.length)];
const randomMonthYear = ['Month', 'Year'];
const randomBreed = [
'Collie',
'Podenco',
'Breton',
'Mestizo',
'German Sheperd',
'Akbash',
'Husky',
'Golden Retriever',
'Basset Hound'
];
const createPets = async (rowsCount) => {
const pets = [];
for (let i = 0; i < rowsCount; i++) {
const {
name: { firstName },
lorem: { paragraph },
image: { animals },
date: { past }
} = faker;
const name = firstName();
const age = randomNumber(1, 20).toString();
const ageMonthYear = randomArray(randomMonthYear);
const weight = randomNumber(1, 80).toString();
const img = animals();
const breed = randomArray(randomBreed);
const dateArrivalInShelter = past();
const about = paragraph();
pets.push(
new PetModel({
name,
age,
ageMonthYear,
weight,
img,
breed,
dateArrivalInShelter,
about
})
);
}
await PetModel.insertMany(pets);
console.info('> pets created!');
const shelters = await ShelterModel.find({});
let prevIndex = 0;
const shelterPromises = shelters
.map((shelter) => {
const petAmount = randomNumber(1, 10);
const randomPets = pets.slice(prevIndex, petAmount);
if (randomPets.length) {
prevIndex = prevIndex + petAmount;
return ShelterModel.findByIdAndUpdate(shelter._id, {
dogs: randomPets
});
}
return null;
})
.filter(Boolean);
await Promise.all(shelterPromises);
console.info('> shelters updated!');
};
const dropPets = async () => {
await PetModel.deleteMany({});
console.info('> pets collection deleted!');
};
module.exports = {
createPets,
dropPets
};
Because your prevIndex is increasing at prevIndex = prevIndex + petAmount but petAmount is always in range 1->10 so after some loop, prevIndex will be greater than petAmount and pets.slice(prevIndex, petAmount) will just return an empty array. To fix this, you can change your code to:
const randomPets = pets.slice(prevIndex, prevIndex + petAmount);
I am not good at English. Successfully make recursive call function. However, there is a memory leak for some reason. The question is that there is no return. The purpose of this feature is to view and explore objects, arrays, and the rest of their properties.
How do I change the code if I have a problem with my return?
Thank you in advance.
I was able to know the cause of the memory leak through Google dev tools profiles.
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
recursionProperty(null, {foo:'bar', baz: ['x','y']}, (prop, obj) => console.log(prop, obj));
my original code
import _ from 'lodash';
import fs from 'fs';
import path from 'path';
import errors from '#feathersjs/errors';
import connections from '../../../connections';
import config from '../../../config';
/**
* #param req
* #param serviceItem
* #param query
* #returns {Promise<any>}
*/
const getServicePromise = async (req, serviceItem, query) => {
let serviceName = serviceItem;
if (typeof serviceItem !== 'string') {
serviceName = `datasets/${serviceItem.name}`;
}
return new Promise(async (resolve, reject) => {
let result;
let objResult;
try {
result = await req.app.service(serviceName).find(query);
} catch (e) {
result = null;
console.log(e);
}
// console.log(result);
if (result) {
if (typeof serviceItem !== 'string') {
objResult = { [serviceItem.name]: result.data };
} else {
objResult = { [serviceName]: result.data };
}
resolve(objResult);
} if (result === null) {
objResult = { [serviceName]: [] };
resolve(objResult);
} else {
reject({
error: 'Not found data.'
});
}
});
};
/**
* 파일 경로 프로퍼티를 찾는 재귀함수
* 객체, 배열, 원시타입 등 여러 타입이 섞여있어도 사용 가능
* #param prop
* #param obj
* #param fn
*/
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
/**
* #param req
* #returns {Promise<{any}>}
*/
const getService = async req => {
const result = {};
const serverPath = [];
const { sheet, dataset, icon } = req.data;
const iconState = Object.prototype.hasOwnProperty.call(req.data, 'icon');
const sheetState = Object.prototype.hasOwnProperty.call(req.data, 'sheet');
const datasetState = Object.prototype.hasOwnProperty.call(req.data, 'dataset');
try {
// sheets
if (sheetState) {
const itemList = ['sheets'];
if (sheet.length === 0) {
const query = {
query: {
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else if (sheet.length > 0) {
const query = {
query: {
_id: {
$in: sheet,
},
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else {
result.sheets = [];
}
} else {
result.sheets = [];
}
// 파일 경로 구하기
if (sheet) {
const { sheets } = result;
// const filePath = [];
recursionProperty('files', sheets, (prop, value) => {
// 여기서 원하는 필드인지 검색후 처리함
if (prop === 'fullPath' && fs.existsSync(path.join(__dirname, '../../../../files', value))) {
// filePath.push(path.join(__dirname, '../../../../files', value));
serverPath.push(value);
}
});
// const deduplication = Array.from(new Set(serverPath));
// const deduplicationPath = await deduplicationFilePath(deduplication);
//
// Object.assign(result, { filePath: deduplicationPath });
} else {
// result.filePath = [];
}
// files
if (sheet) {
const deduplicationFiles = Array.from(new Set(serverPath));
if (deduplicationFiles.length > 0) {
const query = {
query: {
$sort: {
createdAt: -1,
},
fullPath: {
$in: deduplicationFiles,
},
}
};
const files = await req.app.service('files').find(query);
Object.assign(result, { files: files.data });
} else {
result.files = [];
}
} else {
result.files = [];
}
// dataset
if (datasetState) {
const query = {
query: {
// $limit: 100000
}
};
if (dataset.length === 0) {
const meta = await req.app.service('datasets/_meta_').find();
Object.assign(result, { _meta_: meta });
const db = await connections.getConnection(connections.DATASETS_DB);
const collectionNames = _.filter(await db.client.db(config.database_datasets.dbname).listCollections().toArray(), o => o.name !== '_meta_');
// collectionNames.forEach(str => {
// const detectA = iconvDetect.detect(Buffer.from(str.name));
// console.log('collection type', str.name, detectA);
// });
await Promise.all(meta.map(serviceItem => {
// const detectA = iconvDetect.detect(Buffer.from(serviceItem.key));
// console.log('meta type', serviceItem.key, detectA);
return getServicePromise(req, `datasets/${serviceItem.key}`, query);
})).then(data => {
Object.assign(result, { datasets: data });
});
} else if (dataset.length > 0) {
const metaQuery = {
query: {
$sort: {
createdAt: -1,
},
key: {
$in: dataset
}
}
};
const meta = await req.app.service('datasets/_meta_').find(metaQuery);
// console.log(meta);
Object.assign(result, { _meta_: meta });
await Promise.all(dataset.map(serviceItem => getServicePromise(req, `datasets/${serviceItem}`, query))).then(data => {
const d = Array.from(new Set(data));
const s = d.filter(item => item !== null);
if (d.length > 0) {
Object.assign(result, { datasets: s });
} else {
result.datasets = [];
result._meta_ = [];
}
});
} else {
result.datasets = [];
result._meta_ = [];
}
} else {
result.datasets = [];
result._meta_ = [];
}
if (iconState) {
const itemList = ['iconCategories', 'iconItems'];
const query = {};
if (icon.length === 0) {
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
}
} else {
result.iconCategories = [];
result.iconItems = [];
}
} catch (e) {
throw new errors.BadRequest('The data is invalid.', e);
}
return result;
};
export default getService;
There is most likely no memory leak in your code. Yes, recursive functions can be more memory aggressive than normal functions, because the call stack can grow very quickly, but remember that all functions will implicitly return even if there is no return statement. (Imagine that there is always a return undefined; line at the end of all your functions)
When doing a recursion, call stack will grow until you reach the bottom of a recursion branch (no function returns until then). Once the recursion branch end is reached, in your case this happens anytime you reach your else block, call stack will 'collapse' and all functions preceding will 'return'.
Memory will ultimately be freed by garbage collection as required.
I have two properties that might depend on each other so I introduce a bus:
let esDealOne1 = Bacon.later(0, true);
let esDealOne2 = Bacon.later(1000, true);
let bHanging = new Bacon.Bus();
let esSetCards = bHanging.filter(_ => _.cards);
let pHanging = HangingStateProperty(esSetCards);
let pDrawer = DrawerProperty(bHanging, esDealOne1);
let pStacks = [
StackProperty(0, esDealOne2, pHanging),
];
function HangingStateProperty(esSetCards) {
let setCards = (_, cards) => {
return { ..._, ...cards };
};
return Bacon.update({},
[esSetCards, setCards]);
}
function DrawerProperty(bHanging, esDealOne1) {
let dealOne1 = (_) => {
let card = _.usefulProperty;
bHanging.push({ cards: [card] });
return _; };
return Bacon.update({},
[esDealOne1, dealOne1]);
}
function StackProperty(n, esAllDealOne2, pHanging) {
let esDealOne2 = esAllDealOne2.filter(_ => _.i === n);
let dealOne2 = (_, {i}, hangingState) => {
let cards = hangingState.cards;
_.property = cards;
return _;
};
return Bacon.update({},
[esDealOne2, pHanging, dealOne2]);
}
How can I get rid of this bus?
It's hard to remove the bus, instead this would simplify the usage of the Bus:
export function SyncBus() {
let bus = this.bus = new Bacon.Bus();
this.assign = pBase => {
pBase.onValue(_ => bus.push(_));
};
}
Usage:
let bDrawer = new SyncBus();
// don't forget `toProperty` otherwise gets old values
let pDragCardsDrawEarly = bDrawer
.bus
.map(_ => _.extra)
.filter(_ => _.dragcards)
.map(_ => ({ dragcards: _.dragcards }))
.toProperty();
let pDrawer = OwnerProperty({});
bDrawer.assign(pDrawer);