Handle all promise rejections in nested promises? [duplicate] - javascript

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);
};

Related

Run Script as new content loaded , how can i listen to DOM change?

Hi im working on a Chrome extension that adds badges to users, now the problem is i have to refresh the page everytime so the badges can load because there are client-side changes . How can i watch events change so i run the function on first time page load ?
I read something about input event listener or MutationObserver but im not sure how can i implement that , Any help would be much appreciated .
CODE:
function Foreground() {
let users = null;
let queries = [];
let userIds = [];
document.addEventListener("DOMContentLoaded", function(event) {});
window.addEventListener('load', () => {
fetchUsersAndQueries();
chrome.runtime.onMessage.addListener(async(message, sender, res) => {
if (message.users) {
fetchUsersAndQueries();
if (users) {
return res(users);
} else {
return res([]);
}
}
if (message.refresh) {
try {
assignBadges();
} catch (error) {
console.log(error.message);
}
}
return true;
});
let done = false;
setInterval(() => {
if (done) {
return;
}
if (users) {
done = true;
try {
assignBadges();
} catch (error) {
console.log(error.message);
}
}
}, 500);
});
async function fetchUsersAndQueries() {
userIds = await getAPIUserIds();
let isStop = false;
setInterval(() => {
if (isStop) {
return;
}
const parasiteContainer = document.querySelector('#parasite-container');
if (parasiteContainer) {
if (parasiteContainer.shadowRoot) {
try {
const roster1 = parasiteContainer.shadowRoot.querySelector(`[name="roster1"]`);
const roster2 = parasiteContainer.shadowRoot.querySelector(`[name="roster2"]`);
if (!roster1) {
return
};
if ([...roster1.children].length === 1) {
if (roster1.firstElementChild.children.length === 1) {
if (roster1.firstElementChild.firstElementChild.length === 1) {
const fTeam = [...roster1.firstElementChild.firstElementChild.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.firstElementChild.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.firstElementChild.firstElementChild.children]);
queries.push([...roster2.firstElementChild.firstElementChild.firstElementChild.children]);
isStop = true;
} else {
const fTeam = [...roster1.firstElementChild.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.firstElementChild.children]);
queries.push([...roster2.firstElementChild.firstElementChild.children]);
isStop = true;
}
} else {
const fTeam = [...roster1.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.children]);
queries.push([...roster2.firstElementChild.children]);
isStop = true;
}
}
queries.forEach(query => {
query.map(item => {
if (item.children.length > 1) {
[...item.children].map(child => {
// const container = child.querySelector('.sc-hCQDas'); // Classname might change in the future.
const container = child.firstElementChild ? .firstElementChild ? .firstElementChild ? .firstElementChild ? .children[2];
if (container) {
container.insertAdjacentHTML(
'beforeend',
createBadge(badgesResponse.data.exists, child.dataset.userId)
);
}
});
} else {
// const container = item.querySelector('.sc-hCQDas'); // Classname might change in the future.
const container = item.firstElementChild ? .firstElementChild ? .firstElementChild ? .children[0];
if (container) {
container.insertAdjacentHTML(
'beforeend',
createBadge(badgesResponse.data.exists, item.dataset.userId)
);
}
}
});
});
}

Check two async boolean variables and call a method if both are satisfied

How could I improve this method of rendering only when both variables are met as true, to allow the renderFilters() method to be called:
These two variables are filled asynchronously through 2 API methods:
//getManager()
this.isLoadingManager = true;
//getPdiPOrganization()
this.isLoadingPdiOrganization = true;
promiseRender() {
let interval = setInterval(() => {
if (this.isLoadingManager && this.isLoadingPdiOrganization) {
clearInterval(interval);
this.renderFilters();
} else {
setTimeout(() => {
clearInterval(interval);
this.renderFilters();
}, 5000)
}
}, 500);
}
The problem is that it's very slow... it's calling long after the APIs are called...
Maybe some feature of angular itself, if anyone has a better solution...
const observable = forkJoin({
loading1:this.isLoadingManager,
loading2:this.isLoadingPdiOrganization
});
observable.subscribe({
next: (results) => {
const obs1Val = results[0];
const obs2Val = results[1];
if (obs1Val && obs2Val) {
this.renderFilters();
}
}
})
Or:
const myObservable = Observable.of(this.isLoadingManager && this.isLoadingPdiOrganization);
const myObserver = {
next: (result: Boolean) => this.renderFilters(),
};
myObserver.next(true);
myObservable.subscribe(myObserver);
Adding the methods:
getManager() {
if (this.fromAdminPage && localStorage.getItem('_receivers_pdi')) {
this.meetingService.getIsManager()
.subscribe(res => {
this.showPdiToastNotification = res;
this.isLoadingManager = true;
});
}
}
getPdiPOrganization() {
const url = this.publicEndpoint ? 'current/organization/pdi/configuration' : 'api/current/organization/pdi/configuration';
const requestOptions = {
params: new CustomHttpParams({ isPublicTokenUrl: this.publicEndpoint })
};
this.http.get<any>(url, requestOptions).subscribe(resp => {
this.isLoadingPdiOrganization = true;
this.pdiOrgConfig = resp || {};
this.updatePdiReferenceType(this.pdiOrgConfig);
});
}
You can use forkjoin to subscribe to two observables at the same time. I would stick with using RxJs operators for cases like these. You can read more about forkJoin here.
forkJoin([obs1, obs2]).subscribe({
next: (results) => {
const obs1Val = results[0];
const obs2Val = results[1];
if (obs1Val && obs2Val) {
this.renderFilters();
}
}
});

How to properly extend the Promise class?

I have a custom Promise2 class that extends Promise class to allow early settling. It uses my custom Timer class to check the progress of a simulated activity through another timer t1. In my example, p1 does an early settling but the problem is with the p1.then( doesn't recognize the onfulfilled callback as a function.
I suspected I have to override then() and call the super.then() but it didn't work. By the way, the timed executor callback inside super() is just a workaround to make this accessible. Any ideas on what's lacking in my Promise2 class?
JavaScript Code
'use strict';
const p1 = new Promise2(
(resolve, reject) => {
const t1 = Timer.create(
() => {
resolve('Promise resolved.');
// reject(new Error('Promise rejected.'));
},
3000,
);
return { timer: t1 };
},
);
Timer.create(
() => {
const { isCompleted } = p1.return.timer;
const { progress } = p1.return.timer;
if (isCompleted === false) {
console.log(`Promise: ${progress} %`);
if (progress > 50) {
p1.resolve('Early resolve.');
// p1.reject(new Error('Early reject.'));
p1.return.timer.stop();
}
}
},
250,
true,
16,
);
// p1.promise.then(
p1.then(
(value) => {
console.log('__resolve__');
console.log(value);
},
)
.catch(
(reason) => {
console.log('__catch__');
console.log(reason);
},
);
Promise2 Class
class Promise2 extends Promise {
constructor(executor = null) {
super(
(resolve, reject) => {
Timer.create(
() => {
this.resolve = resolve;
this.reject = reject;
this.return = executor(resolve, reject);
},
1);
},
);
// this.promise = new Promise(
// (resolve, reject) => {
// this.resolve = resolve;
// this.reject = reject;
// this.return = executor(resolve, reject);
// },
// );
}
static create(executor = null) {
return new Promise2(...arguments);
}
}
Timer Class
class Timer {
constructor(workload = null, milliseconds = 1000, isAutostart = true, repeat = 1, isInterval = false) {
this.workload = workload;
this.milliseconds = milliseconds;
this.isAutostart = isAutostart;
this.repeat = repeat;
this.isInterval = isInterval;
this.startTime = 0;
this.endTime = 0;
this.timeLeft = milliseconds;
this.timeoutId = 0;
this.progress = 0;
this.isCompleted = false;
this.endTimeActual = 0;
this.repeatLeft = repeat;
this.isPaused = false;
this.subTimers = [];
if (isAutostart === true) {
this.start();
}
}
start(thisArg = this) {
thisArg.startTime = Date.now();
thisArg.endTime = thisArg.startTime + thisArg.milliseconds;
const timeoutEndTime = Date.now();
thisArg.watch(thisArg.workload, timeoutEndTime, thisArg);
}
watch(workload = null, timeoutEndTime = 0, thisArg = this) {
if (thisArg.isPaused === true) {
return;
}
const timeoutLag = Date.now() - timeoutEndTime;
thisArg.timeLeft = thisArg.endTime - Date.now() - timeoutLag;
if (thisArg.timeLeft > 0) {
thisArg.progress = 100 - ((thisArg.timeLeft / thisArg.milliseconds) * 100);
const inProgress = 100 - thisArg.progress;
const tick = thisArg.timeLeft / inProgress;
timeoutEndTime = Date.now() + tick;
thisArg.timeoutId = setTimeout(thisArg.watch, tick, thisArg.workload, timeoutEndTime, thisArg);
return;
}
thisArg.stop(thisArg);
workload();
if (thisArg.repeatLeft > 0) {
thisArg.isCompleted = false;
thisArg.start();
}
if (thisArg.isInterval === true) {
thisArg.repeatLeft = 1;
}
if (thisArg.subTimers.length > 0) {
thisArg.subTimers.forEach(
(timer) => {
if (timer.isAutostart === true) {
timer.start();
}
},
);
}
}
stop(thisArg = this) {
clearTimeout(thisArg.timeoutId);
thisArg.isCompleted = true;
thisArg.endTimeActual = Date.now();
thisArg.repeatLeft -= 1;
if (thisArg.isInterval === true) {
thisArg.repeatLeft = 0;
}
}
restart(thisArg = this) {
thisArg.stop();
thisArg.startTime = 0;
thisArg.endTime = 0;
thisArg.timeLeft = thisArg.milliseconds;
thisArg.timeoutId = 0;
thisArg.progress = 0;
thisArg.isCompleted = false;
thisArg.endTimeActual = 0;
thisArg.repeatLeft = thisArg.repeat;
thisArg.start();
}
pause(thisArg = this) {
thisArg.isPaused = true;
}
resume(thisArg = this) {
thisArg.isPaused = false;
const timeoutEndTime = Date.now();
thisArg.watch(thisArg.workload, timeoutEndTime, thisArg);
}
static create(workload = null, milliseconds = 1000, isAutostart = true, repeat = 1, isInterval = false) {
return new Timer(...arguments);
}
static chain(timers = []) {
const timerReferences = Timer.chainWalk(timers);
if (timerReferences[0].isAutostart === true) {
timerReferences[0].start();
}
return timerReferences;
}
static chainWalk(timers = [], timerReferences = [], nextTimer = null) {
if (timers.length === 0) {
return timerReferences;
}
if (timerReferences.length === 0) {
timers = [...timers];
}
const timer = timers.shift();
const {
workload = null,
milliseconds = 1000,
isAutostart = true,
repeat = 1,
isInterval = false,
} = timer;
const newTimer = new Timer(workload, milliseconds, false, repeat, isInterval);
newTimer.isAutostart = isAutostart;
if (timerReferences.length === 0) {
timerReferences.push(newTimer);
[nextTimer] = timerReferences;
} else {
nextTimer.subTimers.push(newTimer);
[nextTimer] = nextTimer.subTimers;
}
timerReferences = Timer.chainWalk(timers, timerReferences, nextTimer);
return timerReferences;
}
static tree(timers = []) {
const timerReferences = Timer.treeWalk(timers);
timerReferences.forEach(
(reference) => {
if (reference.isAutostart === true) {
reference.start();
}
},
);
return timerReferences;
}
static treeWalk(timers = []) {
const timerReferences = [];
timers.forEach(
(timer) => {
const {
workload = null,
milliseconds = 1000,
isAutostart = true,
repeat = 1,
isInterval = false,
subTimers = [],
} = timer;
const newTimer = new Timer(workload, milliseconds, false, repeat, isInterval);
newTimer.isAutostart = isAutostart;
if (Array.isArray(subTimers) === true) {
newTimer.subTimers = Timer.treeWalk(subTimers);
}
timerReferences.push(newTimer);
},
);
return timerReferences;
}
}
Console Output
Promise2 Class (Working Alternative)
class Promise2 {
constructor(executor = null) {
this.promise = new Promise(
(resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
this.return = executor(resolve, reject);
},
);
this.then = function (onfulfilled = null, onrejected = null) {
return this.promise.then(...arguments);
};
this.catch = function (onrejected = null) {
return this.promise.catch(...arguments);
};
this.finally = function (onfinally = null) {
return this.promise.finally(...arguments);
};
}
static create(executor = null) {
return new Promise2(...arguments);
}
}

Delete image read after running script

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?

How to skip undefined/missing values in key-value pairs

I'm trying to build a citation generator from json in an API with data about images, stored in key-value pairs. I can get the data to return to the screen, but it always includes undefined in the citation. Sample manifest returns undefined as the creator since that isn't listed in this particular record. How can I keep any undefined value from being returned? I've tried changing the forEach to map, filtering at allMetadata by string length, using if !== undefined at insertCitation, and versions of those in different spots in the code.
EDIT: updated to provide full code, including print to page
(function () {
'use strict';
const buildCitation = {
buildMetadataObject: async function (collAlias, itemID) {
let response = await fetch('/iiif/info/' + collAlias + '/' + itemID + '/manifest.json');
let data = await response.json()
let allMetadata = data.metadata
let citationData = {};
allMetadata.forEach(function (kvpair) {
if (kvpair.value == undefined) {
return false;
} else if (kvpair.label === 'Title') {
citationData.itemTitle = kvpair.value;
} else if (kvpair.label === 'Creator') {
citationData.itemCreator = kvpair.value;
} else if (kvpair.label === 'Repository') {
citationData.itemRepository = kvpair.value;
} else if (kvpair.label === 'Collection Name') {
citationData.itemCollection = kvpair.value;
} else if (kvpair.label === 'Owning Institution') {
citationData.itemOwning = kvpair.value;
} else if (kvpair.label === 'Date') {
citationData.itemDate = kvpair.value;
} else if (kvpair.label === 'Storage Location') {
citationData.itemStorage = kvpair.value;
}
return true;
});
return citationData;
},
insertCitation: function (data) {
var testTitle = data.itemTitle;
console.log(testTitle);
const itemCite = `Citation: "${data.itemTitle}," ${data.itemDate}, ${data.itemCreator}, ${data.itemCollection}, ${data.itemOwning}, ${data.itemStorage}, ${data.itemRepository}.`;
const citationContainer = document.createElement('div');
citationContainer.id = 'citation';
citationContainer.innerHTML = itemCite;
// CHANGED to innerHTML instead of innerText because you may want to format it at some point as HTML code.
if (testTitle) {
document.querySelector('.ItemView-itemViewContainer').appendChild(citationContainer);
}
}
}
document.addEventListener('cdm-item-page:ready', async function (e) {
const citationData = await buildCitation.buildMetadataObject(e.detail.collectionId, e.detail.itemId);
console.log({ citationData });
buildCitation.insertCitation(citationData);
});
document.addEventListener('cdm-item-page:update', async function (e) {
document.getElementById('citation').remove();
const citationData = await buildCitation.buildMetadataObject(e.detail.collectionId, e.detail.itemId);
console.log({ citationData });
buildCitation.insertCitation(citationData);
});
})();
I've simplified your program. The undefined is coming from the fact that there is no item with label Date
const mappings = {
Date: 'itemDate',
Title: 'itemTitle',
Creator: 'itemCreator',
Repository: 'itemRepository',
'Storage Location': 'itemStorage',
'Owning Institution': 'itemOwning',
'Collection Name': 'itemCollection',
}
async function buildMetadataObject(collAlias, itemID) {
let response = await fetch('https://teva.contentdm.oclc.org/iiif/info/p15138coll25/1421/manifest.json');
let data = await response.json()
return data.metadata.reduce(
(acc, { label, value }) => ({ ...acc, [ mappings[label] ]: value }),
{}
)
}
function insertCitation(data) {
var testTitle = data.itemTitle;
const fieldBlackList = ['itemTitle'];
const itemCite = `Citation: "${data.itemTitle}," ${
Object.values(mappings).reduce((acc, cur) => {
if (fieldBlackList.includes(cur)) return acc;
const value = data[cur];
return value ? [...acc, value] : acc
}, []).join(', ')
}.`;
console.log(itemCite);
}
//MAIN PROGRAM
(async() => {
const citationData = await buildMetadataObject();
insertCitation(citationData);
})()

Categories