function createBtn(text) { let btn = document.createElement('button') btn.innerText = `${text}` return btn } function drawObjTable(parent, obj) { let table = document.createElement('table') table.border = 1 table.style.border = '5px double grey' let closeRow = document.createElement('tr') let closeTd = document.createElement('td') closeTd.style.border = 'none' let closeBtn = createBtn('Close table') closeBtn.style.backgroundColor = 'firebrick' closeBtn.style.color = 'white' closeBtn.onclick = () => parent.removeChild(table) closeTd.append(closeBtn) closeRow.append(closeTd) table.append(closeRow) for (let key in obj) { let tr = document.createElement('tr') let tdKey = document.createElement('td') tdKey.innerText = key tdKey.style.backgroundColor = 'black' tdKey.style.color = 'white' let tdValue = document.createElement('td') tdValue.style.backgroundColor = 'grey' tdValue.style.color = 'whitesmoke' if (obj[key].constructor.name === 'Array') { for (let item of obj[key]) { if (typeof item === 'string' && /^(https:\/\/|http:\/\/)/.test(item)) { let btn = createBtn(`Loading...`) btn.disabled = true fetch(item) .then((res) => { if(res.ok) { return res.json() } else { throw new Error() } }) .then(obj => { btn.disabled = false btn.innerText = obj[Object.keys(obj)[0]] btn.onclick = () => { drawObjTable(container, obj) } }) .catch(e => console.warn('something bad happened ', e)) tdValue.append(btn) } else { tdValue.innerText = item } } } else if (typeof obj[key] === 'string' && /^(https:\/\/|http:\/\/)/.test(obj[key])) { let btn = createBtn(key) btn.disabled = true fetch(obj[key]) .then((res) => { if(res.ok) { return res.json() } else { throw new Error() } }) .then(obj => { btn.disabled = false btn.innerText = obj[Object.keys(obj)[0]] btn.onclick = () => { drawObjTable(container, obj) } }) .catch(e => console.warn('something bad happened ', e)) tdValue.append(btn) } else { tdValue.innerText = obj[key] } tr.append(tdKey, tdValue) table.append(tr) } parent.append(table) } let container = document.createElement('div') document.body.append(container) //luke skywalker fetch('https://swapi.dev/api/people/1/') .then((res) => { if(res.ok) { return res.json() } else { throw new Error() } }) .then(luke => { drawObjTable(container, luke) }) .catch((e) => console.warn('something bad happpened ', e)) // myFetch function myFetch(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.onreadystatechange = () => { if (xhr.readyState != 4){ return; } if (xhr.status == 200){ resolve(JSON.parse(xhr.response)) } else { reject(new Error(`smth bad happened during request to ${url}. ${xhr.responseText}`)) } } xhr.onerror = () => { reject(new Error(`smth bad happened during request to ${url}. ${xhr.responseText}`)) } xhr.send() }) } //darth vader myFetch('https://swapi.dev/api/people/4/') .then((res) => { drawObjTable(container, res) }) .catch((e) => {console.warn('error occured!', e)}) //let builtIn = fetch('https://swapi.dev/api/people/1/') let mine = myFetch('https://swapi.dev/api/people/4/') //при нормальном инете сначал побеждает дилей потом майн всегда //let delayPromise = delay(300) //Promise.race([builtIn, mine]).then((value) => console.log('fastest', value)) function delay(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } //вот таким образом выходит по разному при условии что симулируется slow 3g //по разному но не 50 на 50. let delayPromise = delay(4299) Promise.race([delayPromise, mine]).then((value) => console.log('fastest:', value || 'delay'))