Table of Contents (by category)
- Vanilla JavaScript / Fetch / XMLHttpRequest
- jQuery AJAX patterns
- Axios / modern libraries
- Utilities & helpers
- Advanced / composite patterns
- Error handling, retries, timeouts
- Miscellaneous tricks
- Minimal examples ready to drop in
1. Vanilla JavaScript / Fetch / XMLHttpRequest
// 1. Basic fetch GET (JSON)
fetch('/api/data')
.then(res => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
return res.json();
})
.then(data => console.log(data))
.catch(err => console.error('Fetch error:', err));
// 2. Fetch POST with JSON body
fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice', age: 30 })
})
.then(res => res.json())
.then(data => console.log('Response:', data))
.catch(err => console.error('Error:', err));
// 3. Fetch with query parameters helper
function fetchWithParams(url, params) {
const query = new URLSearchParams(params).toString();
return fetch(`${url}?${query}`);
}
// usage:
fetchWithParams('/api/list', { page: 2, per_page: 10 })
.then(res => res.json())
.then(data => console.log(data));
// 4. Fetch with timeout (using AbortController)
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
options.signal = controller.signal;
const timer = setTimeout(() => controller.abort(), timeout);
return fetch(url, options)
.finally(() => clearTimeout(timer));
}
// usage:
fetchWithTimeout('/api/slow', {}, 3000)
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') console.error('Request timed out');
else console.error(err);
});
// 5. Fetch and stream (reading large responses chunk by chunk)
async function fetchStream(url) {
const res = await fetch(url);
const reader = res.body.getReader();
let done = false;
while (!done) {
const { value, done: d } = await reader.read();
done = d;
if (value) {
// value is a Uint8Array chunk
console.log('Chunk length:', value.length);
// process chunk
}
}
}
// usage
fetchStream('/api/largefile');
// 6. XMLHttpRequest basic GET
function xhrGet(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) callback(null, xhr.responseText);
else callback(new Error(`Status: ${xhr.status}`));
};
xhr.onerror = () => callback(new Error('Network error'));
xhr.send();
}
// usage
xhrGet('/api/hello', (err, text) => {
if (err) console.error(err);
else console.log(text);
});
// 7. XMLHttpRequest POST (form URL-encoded)
function xhrPost(url, dataObj, callback) {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) callback(null, xhr.responseText);
else callback(new Error(`Status: ${xhr.status}`));
};
xhr.onerror = () => callback(new Error('Network error'));
const body = Object.entries(dataObj).map(
([k,v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`
).join('&');
xhr.send(body);
}
// usage
xhrPost('/api/save', { name: 'Bob', city: 'NY' }, (err, res) => {
if (err) console.error(err);
else console.log(res);
});
// 8. Polling (repeat request every interval)
function poll(url, interval = 5000, onData, onError) {
async function doPoll() {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`Bad status ${res.status}`);
const data = await res.json();
onData(data);
} catch(err) {
if (onError) onError(err);
} finally {
setTimeout(doPoll, interval);
}
}
doPoll();
}
// usage
poll('/api/status', 3000, data => {
console.log('Status update:', data);
}, err => {
console.warn('Polling error:', err);
});
// 9. Debounced fetch (delay until user stops typing)
function debounce(fn, wait = 300) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), wait);
};
}
const debouncedSearch = debounce(async (term) => {
const res = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
const results = await res.json();
console.log(results);
}, 500);
// usage: call debouncedSearch(inputValue) on input event
// 10. Combine multiple fetches (in parallel) & aggregate results
Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/orders').then(r => r.json())
])
.then(([users, orders]) => {
console.log('Users:', users);
console.log('Orders:', orders);
})
.catch(err => console.error('Error in one of the fetches:', err));
// 11. Sequential fetch chaining
fetch('/api/start')
.then(r => r.json())
.then(data => {
return fetch(`/api/next?from=${data.next}`);
})
.then(r => r.json())
.then(data2 => {
console.log('Chained result:', data2);
})
.catch(err => console.error(err));
// 12. Fetch with retry logic
async function fetchWithRetry(url, options = {}, retries = 3, delayMs = 1000) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options);
if (!res.ok) throw new Error(`Bad status ${res.status}`);
return res;
} catch (err) {
if (i === retries - 1) throw err;
await new Promise(r => setTimeout(r, delayMs));
}
}
}
// usage
fetchWithRetry('/api/flaky', {}, 5, 500)
.then(r => r.json())
.then(data => console.log(data))
.catch(err => console.error('Final failure:', err));
// 13. Fetch POST form data (multipart/form-data)
async function postFormData(url, formElement) {
const formData = new FormData(formElement);
const res = await fetch(url, {
method: 'POST',
body: formData
});
return res.json();
}
// usage: postFormData('/api/upload', document.querySelector('#myform')).then(...)
// 14. Abortable fetch (cancel request)
const controller = new AbortController();
fetch('/api/long', { signal: controller.signal })
.then(r => r.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') console.log('Fetch aborted');
else console.error(err);
});
// to cancel:
controller.abort();
// 15. Using fetch for text / HTML (instead of JSON)
fetch('/api/page.html')
.then(res => res.text())
.then(html => {
document.getElementById('container').innerHTML = html;
});
// 16. Fetch HEAD request (check metadata)
fetch('/api/photo.jpg', { method: 'HEAD' })
.then(res => {
console.log('Content-Length:', res.headers.get('Content-Length'));
});
// 17. Fetch JSON only if modified (using ETag / If-None-Match)
async function fetchIfModified(url, lastEtag = null) {
const headers = {};
if (lastEtag) headers['If-None-Match'] = lastEtag;
const res = await fetch(url, { headers });
if (res.status === 304) return null; // not modified
const newEtag = res.headers.get('ETag');
const data = await res.json();
return { data, etag: newEtag };
}
// usage: store etag and skip re-render if null
// 18. Long polling (repeat only after previous completes)
async function longPoll(url, onData, onError) {
while (true) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`Status ${res.status}`);
const data = await res.json();
onData(data);
} catch (err) {
if (onError) onError(err);
await new Promise(r => setTimeout(r, 1000));
}
}
}
// usage
// longPoll('/api/updates', data => console.log('update:', data));
// 19. Conditional fetch (cache-first)
const cache = new Map();
async function fetchCacheFirst(url) {
if (cache.has(url)) {
return cache.get(url);
}
const res = await fetch(url);
const data = await res.json();
cache.set(url, data);
return data;
}
// usage fetchCacheFirst('/api/user').then(...)
// 20. Streaming upload (for big files)
async function uploadChunked(url, file) {
const chunkSize = 1024 * 512; // 512 KB
for (let start = 0; start < file.size; start += chunkSize) {
const chunk = file.slice(start, start + chunkSize);
const form = new FormData();
form.append('chunk', chunk);
form.append('start', start);
await fetch(url, { method: 'POST', body: form });
}
}
// usage: uploadChunked('/api/upload-chunk', fileInput.files[0]);
2. jQuery AJAX patterns
(These snippets are classic and very widely used. Inspired by existing gist / references.)
// 21. Basic $.ajax POST (JSON)
$.ajax({
url: '/api/endpoint',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ foo: 'bar' }),
dataType: 'json',
success: function(res) {
console.log('Success:', res);
},
error: function(jqXHR, status, err) {
console.error('AJAX error:', status, err);
}
});
// 22. $.ajax GET (with query params)
$.ajax({
url: '/api/users',
method: 'GET',
data: { page: 2, per_page: 20 },
dataType: 'json',
success: function(res) {
console.log(res);
},
error: function(xhr) {
console.error('Error:', xhr.status);
}
});
// 23. Shortcut $.get / $.post
$.get('/api/status', { id: 5 }, function(res) {
console.log(res);
});
$.post('/api/login', { user: 'alice', pass: '1234' })
.done(res => console.log(res))
.fail(err => console.error('Login failed:', err));
// 24. $.getJSON (expects JSON)
$.getJSON('/api/data', { q: 'search' }, function(res) {
console.log('JSON:', res);
});
// 25. Load HTML into container
$('#container').load('/partial.html', function(response, status, xhr) {
if (status === 'error') {
console.error('Load error:', xhr.status, xhr.statusText);
}
});
// 26. Before send / progress / completion
$.ajax({
url: '/api/do',
method: 'POST',
data: { x: 1 },
beforeSend: function(jqXHR, settings) {
$('#spinner').show();
},
success: function(res) { console.log(res); },
error: function(err) { console.error(err); },
complete: function() {
$('#spinner').hide();
}
});
// 27. Global AJAX handlers (start, stop)
$(document).ajaxStart(() => {
$('#globalLoader').show();
});
$(document).ajaxStop(() => {
$('#globalLoader').hide();
});
// 28. Using this.custom property (passing extra data)
$.ajax({
url: '/api/list',
method: 'GET',
myCustomIndex: 5,
success: function(data) {
console.log('Index was', this.myCustomIndex);
}
});
// 29. Cache JSONP / cross-domain
$.ajax({
url: 'https://another.com/api?callback=?',
dataType: 'jsonp',
success: function(data) {
console.log('Cross-domain JSONP data:', data);
}
});
// 30. Abort a jQuery AJAX
const req = $.ajax({
url: '/api/long',
method: 'GET'
});
// later
req.abort();
// 31. Multiple AJAX with $.when / $.Deferred
$.when(
$.ajax('/api/a').done(a => console.log('A done', a)),
$.ajax('/api/b').done(b => console.log('B done', b))
).then(function(aRes, bRes) {
console.log('Both done', aRes, bRes);
}).fail(function() {
console.error('One failed');
});
// 32. Polling with jQuery AJAX
function jqPoll(url, interval, onSuccess, onError) {
function go() {
$.ajax({
url,
method: 'GET',
success: function(data) {
onSuccess(data);
},
error: function(err) {
if (onError) onError(err);
},
complete: function() {
setTimeout(go, interval);
}
});
}
go();
}
// usage
jqPoll('/api/status', 4000, data => console.log(data));
// 33. Serialize form and send via AJAX
$('#myform').on('submit', function(e) {
e.preventDefault();
const form = $(this);
$.ajax({
url: form.attr('action'),
method: form.attr('method'),
data: form.serialize(),
success: function(res) {
console.log('Submitted:', res);
}
});
});
// 34. AJAX file upload using FormData (with jQuery)
$('#uploadForm').on('submit', function(e) {
e.preventDefault();
const form = this;
const fd = new FormData(form);
$.ajax({
url: form.action,
method: form.method,
data: fd,
processData: false,
contentType: false,
success: function(res) {
console.log('File uploaded:', res);
}
});
});
// 35. Fallback to iframe upload (older browsers) — minimal scaffold
function fallbackIFrameUpload(form, callbackName) {
const iframe = document.createElement('iframe');
iframe.name = 'upload_iframe_' + Date.now();
iframe.style.display = 'none';
document.body.appendChild(iframe);
form.target = iframe.name;
window[callbackName] = function(resp) {
// cleanup
delete window[callbackName];
document.body.removeChild(iframe);
callback(resp);
};
form.submit();
}
// usage: wrap your form and server returns a script calling the callbackName
// 36. Error handler helper
function showAjaxError(xhr, status, err) {
console.error('AJAX error:', status, err);
try {
const resp = JSON.parse(xhr.responseText);
console.error('Details:', resp);
} catch (e) {
console.error('Non-JSON response:', xhr.responseText);
}
}
// 37. Chain multiple AJAX (nested)
$.ajax({ url: '/api/step1' })
.done(data1 => {
return $.ajax({ url: `/api/step2?id=${data1.id}` });
})
.done(data2 => {
console.log('All done:', data2);
})
.fail(err => {
console.error('Chain failed:', err);
});
// 38. Abort with timeout in jQuery
$.ajax({
url: '/api/slow',
timeout: 5000, // milliseconds
success: res => console.log(res),
error: (xhr, status) => {
if (status === 'timeout') console.warn('Request timeout');
else console.error('Error:', status);
}
});
// 39. Prevent double submission (disable button)
$('#submitBtn').on('click', function(e) {
const btn = $(this);
btn.prop('disabled', true);
$.ajax({
url: '/api/do',
method: 'POST',
data: { x: 1 },
complete: () => btn.prop('disabled', false)
});
});
// 40. JSON POST with fallback to traditional POST
function postJsonOrForm(url, data) {
if (window.fetch) {
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
} else {
return $.ajax({
url,
method: 'POST',
data: data,
dataType: 'json'
});
}
}
3. Axios / modern libraries
// 41. Basic Axios GET
axios.get('/api/data')
.then(response => console.log(response.data))
.catch(error => console.error(error));
// 42. Axios POST JSON
axios.post('/api/submit', { name: 'Eve', city: 'LA' })
.then(resp => console.log(resp.data))
.catch(err => console.error(err));
// 43. Axios with config (headers, timeout)
axios({
method: 'post',
url: '/api/do',
data: { x: 1 },
headers: { 'X-Custom': 'MyVal' },
timeout: 5000
})
.then(r => console.log(r.data))
.catch(err => console.error(err));
// 44. Axios GET with params
axios.get('/api/users', { params: { page: 3 } })
.then(r => console.log(r.data));
// 45. Axios cancel token (abort)
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/api/long', { cancelToken: source.token })
.then(r => console.log(r.data))
.catch(thrown => {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
console.error(thrown);
}
});
// to cancel:
source.cancel('Operation canceled by user.');
// 46. Axios interceptors (request / response)
axios.interceptors.request.use(config => {
// e.g. add auth header
config.headers['Authorization'] = 'Bearer ' + token;
return config;
}, err => Promise.reject(err));
axios.interceptors.response.use(resp => {
return resp;
}, err => {
if (err.response && err.response.status === 401) {
// handle unauthorized, e.g. redirect to login
}
return Promise.reject(err);
});
// 47. Axios concurrent requests (Promise.all)
Promise.all([
axios.get('/api/a'),
axios.get('/api/b')
]).then(([a, b]) => {
console.log(a.data, b.data);
});
// 48. Axios retry with interceptors (simple)
function axiosRetry(instance, retries = 3) {
instance.interceptors.response.use(null, (error) => {
const config = error.config;
if (!config || !config.retryCount) config.retryCount = 0;
if (config.retryCount >= retries) return Promise.reject(error);
config.retryCount++;
return new Promise(resolve =>
setTimeout(() => resolve(instance(config)), 1000)
);
});
}
axiosRetry(axios, 2);
// 49. Axios upload with progress
const form = new FormData();
form.append('file', fileInput.files[0]);
axios.post('/api/upload', form, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: progressEvent => {
const pct = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log('Upload:', pct + '%');
}
}).then(r => console.log(r.data));
// 50. Axios download with blob
axios.get('/api/report', { responseType: 'blob' })
.then(resp => {
const url = URL.createObjectURL(resp.data);
const a = document.createElement('a');
a.href = url;
a.download = 'report.pdf';
a.click();
});
4. Utilities & Helpers
// 51. Convert object to query string
function toQueryString(obj) {
return Object.entries(obj)
.map(([k,v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
}
// usage: toQueryString({ a:1, b:2 }) → "a=1&b=2"
// 52. Sleep / delay
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 53. Wrap callback-style function into Promise
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, res) => {
if (err) reject(err);
else resolve(res);
});
});
};
}
// 54. Parse JSON safely
function safeJsonParse(str, fallback = null) {
try {
return JSON.parse(str);
} catch (e) {
return fallback;
}
}
// 55. Deep merge of objects (simple)
function deepMerge(...objs) {
const result = {};
for (const obj of objs) {
for (const [k, v] of Object.entries(obj)) {
if (v && typeof v === 'object' && !Array.isArray(v)) {
result[k] = deepMerge(result[k] || {}, v);
} else {
result[k] = v;
}
}
}
return result;
}
// 56. Request queue (limit concurrent fetches)
class RequestQueue {
constructor(maxConcurrency = 3) {
this.maxConcurrency = maxConcurrency;
this.running = 0;
this.queue = [];
}
enqueue(fn) {
return new Promise((res, rej) => {
this.queue.push({ fn, res, rej });
this.next();
});
}
next() {
if (this.running >= this.maxConcurrency) return;
const job = this.queue.shift();
if (!job) return;
this.running++;
job.fn()
.then(job.res)
.catch(job.rej)
.finally(() => {
this.running--;
this.next();
});
}
}
// usage
// const rq = new RequestQueue(2);
// rq.enqueue(() => fetch('/api/a')).then(...);
// 57. Once-only wrapper
function once(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
result = fn.apply(this, args);
called = true;
}
return result;
};
}
// 58. Retry wrapper for fetch-like promises
function withRetry(fn, retries = 2, delayMs = 1000) {
return async function wrapped(...args) {
let lastError;
for (let i = 0; i < retries; i++) {
try {
return await fn(...args);
} catch (err) {
lastError = err;
await sleep(delayMs);
}
}
throw lastError;
};
}
// 59. Cache results of fetch (memoize)
function memoizeFetch(fn) {
const cache = new Map();
return async function(url, options) {
const key = url + JSON.stringify(options || {});
if (cache.has(key)) {
return cache.get(key);
}
const res = await fn(url, options);
const data = await res.clone().json();
cache.set(key, data);
return new Response(JSON.stringify(data), {
status: res.status,
statusText: res.statusText,
headers: res.headers
});
};
}
// 60. Exponential backoff (retry with increasing delay)
async function fetchWithBackoff(url, options = {}, maxRetries = 5, baseDelay = 500) {
for (let i = 0; i < maxRetries; i++) {
try {
const res = await fetch(url, options);
if (!res.ok) throw new Error(`Bad status ${res.status}`);
return res;
} catch (err) {
if (i === maxRetries - 1) throw err;
const wait = baseDelay * 2 ** i;
await sleep(wait);
}
}
}
5. Advanced / composite patterns
// 61. AJAX + UI loading indicator (vanilla)
async function fetchWithLoader(url, container, loaderEl) {
loaderEl.style.display = 'block';
try {
const res = await fetch(url);
const data = await res.json();
container.innerText = JSON.stringify(data, null, 2);
} catch (err) {
container.innerText = 'Error: ' + err.message;
} finally {
loaderEl.style.display = 'none';
}
}
// 62. Infinite scroll (on scroll bottom, fetch more)
let currentPage = 1;
let loading = false;
window.addEventListener('scroll', async () => {
if (loading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
loading = true;
try {
const res = await fetch(`/api/list?page=${++currentPage}`);
const items = await res.json();
items.forEach(item => {
const el = document.createElement('div');
el.textContent = JSON.stringify(item);
document.body.appendChild(el);
});
} catch (err) {
console.error('Load more failed:', err);
}
loading = false;
}
});
// 63. Live search / autocomplete
const input = document.querySelector('#searchBox');
input.addEventListener('input', debounce(async (e) => {
const term = e.target.value;
if (!term) return;
const res = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
const suggestions = await res.json();
// render suggestions...
console.log('suggestions:', suggestions);
}, 300));
// 64. Dependent dropdowns (select A => fetch options for B)
$('#selectA').on('change', function() {
const val = $(this).val();
$.ajax({
url: '/api/options',
data: { parent: val },
dataType: 'json',
success: function(arr) {
const $selB = $('#selectB');
$selB.empty();
arr.forEach(o => {
$selB.append(`<option value="${o.value}">${o.label}</option>`);
});
}
});
});
// 65. AJAX pagination (replace page content)
$('.pagination a').on('click', function(e) {
e.preventDefault();
const url = $(this).attr('href');
$('#content').fadeOut(200, function() {
$('#content').load(url + ' #content > *', function() {
$('#content').fadeIn(200);
});
});
});
// 66. Submit via AJAX and show inline validation errors
$('#form').on('submit', function(e) {
e.preventDefault();
const form = $(this);
$.ajax({
url: form.attr('action'),
method: form.attr('method'),
data: form.serialize(),
success: function(res) {
if (res.errors) {
for (const [field, msg] of Object.entries(res.errors)) {
form.find(`[name=${field}]`).after(`<span class="error">${msg}</span>`);
}
} else {
// success path
}
}
});
});
// 67. Optimistic UI update + rollback
async function optimisticUpdate(url, newData, oldData) {
const backup = { ...oldData };
// update UI immediately
Object.assign(oldData, newData);
try {
const res = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newData)
});
if (!res.ok) throw new Error('Save failed');
} catch (err) {
// rollback
Object.assign(oldData, backup);
console.error('Optimistic update failed:', err);
}
}
// 68. Partial HTML update (patch)
async function patchHtml(selector, url) {
const res = await fetch(url);
const html = await res.text();
document.querySelector(selector).innerHTML = html;
}
// 69. JSON PATCH with REST
fetch('/api/item/123', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json-patch+json' },
body: JSON.stringify([
{ op: 'replace', path: '/name', value: 'New name' }
])
}).then(r => r.json()).then(data => console.log(data));
// 70. Batching multiple API calls to single endpoint
async function batchFetch(calls) {
// calls = [{ url, method, body }, ...]
const res = await fetch('/api/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ calls })
});
return res.json(); // expect array of responses
}
// usage
// batchFetch([{ url: '/u/1', method: 'GET' }, { url: '/u/2', method: 'GET' }])
// .then(results => { ... });
// 71. Progressive enhancement: fallback to normal navigation when JS disabled
document.querySelectorAll('a.ajax').forEach(a => {
a.addEventListener('click', e => {
e.preventDefault();
fetch(a.href)
.then(r => r.text())
.then(html => {
document.getElementById('main').innerHTML = html;
// update URL
window.history.pushState({}, '', a.href);
});
});
});
window.addEventListener('popstate', () => {
fetch(location.href)
.then(r => r.text())
.then(html => {
document.getElementById('main').innerHTML = html;
});
});
// 72. Using POST but simulate redirect (for APIs that respond with redirect info)
$.ajax({
url: '/api/login',
method: 'POST',
data: { user: 'u', pass: 'p' },
success: function(res) {
if (res.redirectTo) window.location = res.redirectTo;
else console.log('Login result', res);
}
});
// 73. Cancel an in-flight request when new one is made (debounce + cancel)
let lastRequest = null;
function doSearch(term) {
if (lastRequest) lastRequest.abort();
lastRequest = new AbortController();
fetch(`/api/search?q=${encodeURIComponent(term)}`, {
signal: lastRequest.signal
})
.then(r => r.json())
.then(data => console.log('search:', data))
.catch(err => {
if (err.name === 'AbortError') console.log('Search aborted');
else console.error(err);
});
}
// 74. WebSocket fallback: if server supports real-time updates
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => console.log('Socket open');
ws.onmessage = msg => {
const data = JSON.parse(msg.data);
console.log('WS message:', data);
};
ws.onerror = err => console.error('WS error:', err);
// 75. Detect AJAX complete on document (jQuery)
$(document).ajaxComplete((event, xhr, settings) => {
console.log('AJAX complete:', settings.url, xhr.status);
});
6. Error handling, retries, timeouts, fallback
// 76. Centralized fetch wrapper with error translation
async function safeFetch(url, options) {
try {
const res = await fetch(url, options);
if (!res.ok) {
const text = await res.text();
throw new Error(`HTTP ${res.status}: ${text}`);
}
return res;
} catch (err) {
console.error('safeFetch error:', err);
throw err;
}
}
// 77. Retry with jitter
async function fetchWithJitter(url, options = {}, retries = 4, baseDelay = 300) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options);
if (!res.ok) throw new Error(`Bad status ${res.status}`);
return res;
} catch (err) {
if (i === retries - 1) throw err;
const jitter = Math.random() * baseDelay;
await sleep(baseDelay * i + jitter);
}
}
}
// 78. Fallback to JSONP if CORS fails (vanilla)
function jsonp(url, callbackName) {
return new Promise((resolve, reject) => {
const cb = `jsonp_cb_${Date.now()}`;
window[cb] = data => {
resolve(data);
delete window[cb];
};
const s = document.createElement('script');
s.src = `${url}${url.includes('?') ? '&' : '?'}${callbackName}=${cb}`;
s.onerror = reject;
document.body.appendChild(s);
});
}
// usage: jsonp('https://api.example.com/data', 'callback').then(data => ...)
// 79. Fallback content if AJAX fails
fetch('/api/part')
.then(r => {
if (!r.ok) throw new Error('Bad fetch');
return r.text();
})
.then(html => {
document.getElementById('sidebar').innerHTML = html;
})
.catch(err => {
console.warn('Fallback: show default');
document.getElementById('sidebar').innerHTML = '<p>Offline content here</p>';
});
// 80. Catch-all global fetch error (monkey patch)
const _oldFetch = window.fetch;
window.fetch = function(...args) {
return _oldFetch(...args).catch(err => {
console.error('Global fetch error:', err, 'for args:', args);
throw err;
});
};
7. Miscellaneous tricks & small helpers
// 81. Listen to any AJAX success (jQuery)
$(document).ajaxSuccess((e, xhr, settings) => {
console.log('Successful AJAX:', settings.url);
});
// 82. Serialize form as JSON
function serializeFormToJSON(formElement) {
const form = new FormData(formElement);
const obj = {};
for (const [k, v] of form.entries()) {
obj[k] = v;
}
return obj;
}
// 83. Cancelable promise wrapper (for fetch or AJAX)
function cancelablePromise(promise) {
let canceled = false;
const wrapped = new Promise((res, rej) => {
promise.then(
val => canceled ? rej({ canceled: true }) : res(val),
err => canceled ? rej({ canceled: true }) : rej(err)
);
});
return {
promise: wrapped,
cancel() { canceled = true; }
};
}
// usage
// const c = cancelablePromise(fetch('/api'));
// c.promise.then(...).catch(e => { if (e.canceled) … });
// c.cancel();
// 84. Add timestamp to URL to prevent caching
function cacheBustingUrl(url) {
const sep = url.includes('?') ? '&' : '?';
return `${url}${sep}_=${Date.now()}`;
}
// usage: fetch(cacheBustingUrl('/api/data'));
// 85. Proxy to wrap fetch to always send credentials
const fetchWithCreds = (url, options = {}) => {
return fetch(url, { ...options, credentials: 'include' });
};
// 86. Monitoring long requests (log if > threshold)
async function monitoredFetch(url, options = {}, threshold = 2000) {
const start = Date.now();
const res = await fetch(url, options);
const elapsed = Date.now() - start;
if (elapsed > threshold) {
console.warn(`Request to ${url} took ${elapsed}ms`);
}
return res;
}
// 87. Refresh token on 401 and retry
async function fetchWithAuth(url, options = {}) {
let res = await fetch(url, options);
if (res.status === 401) {
// assume refresh token logic
await fetch('/api/refresh', { method: 'POST' });
res = await fetch(url, options);
}
return res;
}
// 88. Lazy load a script only when needed (inject and await)
function loadScript(src) {
return new Promise((res, rej) => {
const s = document.createElement('script');
s.src = src;
s.onload = res;
s.onerror = rej;
document.head.appendChild(s);
});
}
// 89. Progressive fetch (fetch first few, then rest)
async function progressiveLoad(urls) {
const first = await fetch(urls[0]).then(r => r.json());
console.log('First loaded:', first);
for (let i = 1; i < urls.length; i++) {
fetch(urls[i])
.then(r => r.json())
.then(data => console.log('Loaded:', data))
.catch(err => console.error(err));
}
}
// 90. Fire-and-forget (ignore result, but catch errors)
async function fireAndForget(url, options = {}) {
fetch(url, options).catch(err => console.error('FF error:', err));
}
// usage: fireAndForget('/api/log', { method: 'POST', body: ... });
// 91. CORS preflight check (OPTIONS)
fetch('/api/data', {
method: 'OPTIONS'
}).then(r => {
console.log('Allowed methods:', r.headers.get('Allow'));
});
// 92. Fetch local JSON file
fetch('/assets/data.json')
.then(r => r.json())
.then(data => console.log(data));
// 93. Load CSS file dynamically after fetch
async function loadCssAfterFetch(cssUrl, gateUrl) {
await fetch(gateUrl);
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = cssUrl;
document.head.appendChild(link);
}
// 94. Chunked download (range requests)
async function fetchRange(url, start, end) {
const res = await fetch(url, {
headers: { Range: `bytes=${start}-${end}` }
});
return res.arrayBuffer();
}
// 95. JSON to FormData conversion
function jsonToFormData(obj, fd = new FormData(), prefix = '') {
for (const [k, v] of Object.entries(obj)) {
const key = prefix ? `${prefix}[${k}]` : k;
if (v && typeof v === 'object' && !(v instanceof File)) {
jsonToFormData(v, fd, key);
} else {
fd.append(key, v);
}
}
return fd;
}
// 96. Check if browser supports fetch, fallback to XHR
function myFetch(url, options) {
if (window.fetch) {
return fetch(url, options);
} else {
return new Promise((resolve, reject) => {
xhrGet(url, (err, text) => {
if (err) reject(err);
else resolve({ text: () => text });
});
});
}
}
// 97. Rate limiting requests
function rateLimit(fn, limit = 1000) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last < limit) {
return Promise.reject(new Error('Rate limit exceeded'));
}
last = now;
return fn(...args);
};
}
// 98. Wrap fetch to always return JSON or throw
async function fetchJsonOrThrow(url, options) {
const res = await fetch(url, options);
if (!res.ok) {
const msg = await res.text();
throw new Error(`HTTP ${res.status}: ${msg}`);
}
return res.json();
}
// 99. Lazy-loaded infinite scroll (trigger only when scroll region is visible)
function initInfiniteScroll(container, fetchFn) {
const observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
fetchFn();
}
});
observer.observe(container);
}
// 100. AJAX + form reset + notification
$('#myForm').on('submit', function(e) {
e.preventDefault();
const frm = $(this);
$.ajax({
url: frm.attr('action'),
method: frm.attr('method'),
data: frm.serialize()
}).done(res => {
alert('Saved!');
frm[0].reset();
}).fail(err => {
alert('Error: ' + err.statusText);
});
}
8. Minimal full examples ready to drop in
<!-- 101. Minimal fetch demo -->
<button id="loadBtn">Load Data</button>
<div id="output"></div>
<script>
document.getElementById('loadBtn').addEventListener('click', () => {
fetch('/api/hello')
.then(r => r.json())
.then(data => {
document.getElementById('output').textContent = JSON.stringify(data);
})
.catch(e => {
document.getElementById('output').textContent = 'Error: ' + e;
});
});
</script>
<!-- 102. jQuery AJAX demo -->
<form id="demoForm" action="/api/echo" method="post">
<input name="msg" placeholder="Message" />
<button type="submit">Send</button>
</form>
<div id="resp"></div>
<script>
$('#demoForm').on('submit', function(e) {
e.preventDefault();
const f = $(this);
$.ajax({
url: f.attr('action'),
method: f.attr('method'),
data: f.serialize(),
success: function(res) {
$('#resp').text('Server says: ' + JSON.stringify(res));
},
error: function(xhr) {
$('#resp').text('Error: ' + xhr.statusText);
}
});
});
</script>
// 103. Debounced search + fetch demo (vanilla)
const input = document.getElementById('search');
input.addEventListener('input', debounce(async (e) => {
const q = e.target.value;
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
const arr = await res.json();
console.log('Results:', arr);
}, 300));
Top comments (0)