const url = "https://i.imgur.com/sduLRvf.jpeg";
const resp = await fetch(url);
const blob = await resp.blob();
const name = url.split("/").pop().replace("jpeg", "jpg");
downloadBlob(blob, name, url);
function downloadBlob(blob, name, url) {
const anchor = document.createElement("a");
anchor.setAttribute("download", name || "");
const blobUrl = URL.createObjectURL(blob);
anchor.href = blobUrl + (url ? ("#" + url) : "");
anchor.click();
setTimeout(() => URL.revokeObjectURL(blobUrl), 3000);
}
url
параметр опциональный, чисто чтобы сохранить возможность посмотреть оригинальный URL в менеджере загрузок. console.log((Buffer.from(hex, "hex").toString("utf-8")));
5MA0C��@�Q
[...new TextEncoder().encode("5MA0C��@�Q")].map(n => n.toString(16)).join("")
===
"354d413043efbfbdefbfbd40efbfbd51"
// true
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(100);
}
await sleep(1000);
console.log("Done");
"Done"
. TransformStream
. ReadableStream
. Единственное отличие, что TransformStream
"ленивый". Т.е. если закомментировать await newResponse.blob();
onProgress
не будет вызываться, хотя данные были загружены.const response = await fetch("https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg");
total.value = parseInt(response.headers.get("content-length")) || 0;
function onProgress(chunk) {
loaded.value += chunk.length;
}
const ts = new TransformStream({
transform(chunk, controller) {
onProgress(chunk);
controller.enqueue(chunk);
},
});
response.body.pipeThrough(ts);
const newResponse = new Response(ts.readable);
const blob = await newResponse.blob();
"content-length"
может отсутствовать. "content-encoding"
, который, к слову, по-умолчанию будет недоступен для кода при кросс-доменных запросах."content-encoding"
в "content-length"
значение всегда указано для сжатого респонса, а в коллбеке будут уже не сжатые данные. Т.е. loaded
в таком случае будет больше значения total
("content-length"
)ReadableStream
:const reader = response.body.getReader();
const readableStream = new ReadableStream({
async start(controller) {
while (true) {
const {done, value} = await reader.read();
if (done) { break; }
onProgress(value);
controller.enqueue(value);
}
controller.close();
reader.releaseLock();
},
cancel() {
void reader.cancel();
}
});
const newResponse = new Response(readableStream);
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const btns = [...document.querySelectorAll("button")];
const filteredBtns = btns.filter(btn => btn.textContent.startsWith("Кнопка"));
for (const btn of filteredBtns) {
btn.click();
await sleep(5000);
}
{name: 'Ivan'}
new Uint8Array(100_000_000)
. Весомый.let weakMap = new WeakMap();
let obj = {a: new Uint8Array(100_000_000)};
weakMap.set({}, obj);
WeakMap
Map
.new Uint8Array(1_000_000_000)
.let weakMap = new Map();
не поможет при таком спаме в консоль, т.к. если объект выведен в косколь, он так и будет висеть в памяти, пока консоль не будет очищена (console.clear()
), либо страница перезагружена.WeakHashMap
. onerror
у img
элемента.<img src="not-loading-image.jpg" onerror="this.remove();">
<img src="https://hsto.org/webt/62/a8/df/62a8df7d29053796257182.png" onload="console.log(this);">
async asyncMethod() {
// следующее добавит в очередь микро-тасков:
await null; // или
await Promise.resolve();
// а это добавит в очередь тасков:
await new Promise(resolve => setTimeout(resolve));
// лучше используй setImmediate, он многократно быстрее отработает (без задержки в 4 мс)
console.log("hello from asyncMethod");
}
async
) функции или коллбека конструтора Promise
выполняется синхронно до первого await
.await
, не используй async
.export default {
data() {
return {
dateNow: new Date(),
timerId: null
};
},
mounted() {
this.timerId = setInterval(() => {
this.dateNow = new Date();
}, 1000);
},
beforeUnmount() {
clearInterval(this.timerId);
}
};
import {ref, onUnmounted} from "vue";
const dateNow = ref(new Date());
const timerId = setInterval(() => {
dateNow.value = new Date();
}, 1000);
onUnmounted(() => {
clearInterval(timerId);
});
// element-util.js
export function createElement(options) {
// код функции
}
export function showElement(options) {
// код функции
}
export function hideElement(options) {
// код функции
}
import {createElement} from "./element-util.js";
function Component() {
const heading = createElement({
id: "heading",
content: "Hello World",
});
const message = createElement({
id: "message",
content: "New Article",
});
return {heading, message};
}
requests
использует по-умолчанию (даже без использования Session
), а именно наличию connection: "keep-alive"
, keep alive режим работает из коробки, и, очевидно, правильно.node-fetch
нет. Но это можно исправить, см. мой ответ."authority"
, "method"
, "path"
, "scheme"
.":authority"
, ":method"
, ":path"
, ":scheme"
, псевдо-заголовки HTTP/2.requests
— HTTP/1.1 библиотека.import fetch from "node-fetch";
import http from "node:http";
import https from "node:https";
const httpAgent = new http.Agent({keepAlive: true});
const httpsAgent = new https.Agent({keepAlive: true});
function agent(_parsedURL) {
if (_parsedURL.protocol === "http:") {
return httpAgent;
} else {
return httpsAgent;
}
}
const response = await fetch("https://example.com/", {
agent
});
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
while (true) {
await sleep(10000);
openBox();
await sleep(3000);
const data = JSON.parse(response);
for (const entry of data) {
entry.btn.click(); // рили?
await sleep(3000);
entry.btnClose.click();
}
await sleep(3000);
btn.click();
}
Ну почему-то, после перезагрузки страницы...
append
, after
, createElement
.querySelector
также пригодится для уже существующих нод. Spread types may only be created from object types.
undefined
.useSomething
) делается так — возвращается объект, чьи поля присваиваются другому объкту путем оператора спреад.