Skip to content

Instantly share code, notes, and snippets.

@lmzach09
Last active September 28, 2022 16:29
Show Gist options
  • Save lmzach09/38d291edb90f64d338132824a9de35ce to your computer and use it in GitHub Desktop.
Save lmzach09/38d291edb90f64d338132824a9de35ce to your computer and use it in GitHub Desktop.
Implementation of a for-each loop in JavaScript that works with callbacks. The next iteration in the loop does not start until the previous iteration's callback gets called.
const array = [1, 2, 3, 4, 5];
array.forEachWithCallback((el, i, next) => {
request({
method: 'GET',
hostname: 'httpbin.org',
path: '/get?myArg=' + el
}).then((res) => {
const responseBody = JSON.parse(res.body);
console.log(responseBody.args.myArg);
next();
}).error((err) => {
console.error(err);
});
});
<script src="browser-request-promise.js"></script>
<!-- *** see "browser-request-promise.js" file which is also in this gist *** -->
<script>
function forEachWithCallback(callback) {
const arrayCopy = this;
let index = 0;
const next = () => {
index++;
if (arrayCopy.length > 0) {
callback(arrayCopy.shift(), index, next);
}
}
next();
}
Array.prototype.forEachWithCallback = forEachWithCallback;
const array = [1, 2, 3, 4, 5];
array.forEachWithCallback((el, i, next) => {
window.request({
method: 'GET',
hostname: 'httpbin.org',
path: '/get?myArg=' + el
}).then((res) => {
const responseBody = JSON.parse(res.body);
console.log(responseBody.args.myArg);
next();
}).catch((err) => {
console.error(err);
});
});
</script>
window.request = (options) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
let contentTypeIsSet = false;
options = options || {};
const method = options.method || "GET";
let url = options.hostname;
url += typeof options.path === "string" ? options.path : "";
if (typeof url !== "string") {
return reject("HTTP Request: Invalid URL.");
}
// Use 'https' if the protocol is not specified in 'options.hostname'
if (
url.indexOf("http://") !== 0 &&
url.indexOf("https://") !== 0
) {
url = "https://" + url;
}
xhr.open(method, url);
for (let header in options.headers) {
if ({}.hasOwnProperty.call(options.headers, header)) {
let lcHeader = header.toLowerCase();
contentTypeIsSet = lcHeader === "content-type" ? true : contentTypeIsSet;
xhr.setRequestHeader(header, options.headers[header]);
}
}
if (!contentTypeIsSet) {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
xhr.onload = function() {
let body;
if (xhr.status >= 100 && xhr.status < 400) {
try {
JSON.parse(xhr.response);
body = xhr.response;
} catch (e) {
body = xhr.statusText;
}
return resolve({
status: xhr.status,
statusText: xhr.statusText,
body
});
} else {
return reject({
status: xhr.status,
statusText: xhr.statusText
});
}
};
if (method !== "GET") {
xhr.send(JSON.stringify(options.body));
} else {
xhr.send();
}
});
};
// Node.js example of a synchronous loop with promises in loop body
const request = require('./request.js'); // see "request.js" file which is also in this gist
function forEachWithCallback(callback) {
const arrayCopy = this;
let index = 0;
const next = () => {
index++;
if (arrayCopy.length > 0) {
callback(arrayCopy.shift(), index, next);
}
}
next();
}
Array.prototype.forEachWithCallback = forEachWithCallback;
const array = [1, 2, 3, 4, 5];
array.forEachWithCallback((el, i, next) => {
request({
method: 'GET',
hostname: 'httpbin.org',
path: '/get?myArg=' + el
}).then((res) => {
const responseBody = JSON.parse(res.body);
console.log(responseBody.args.myArg);
next();
}).catch((err) => {
console.error(err);
});
});
// This file declares a generic "request" method that works in both the browser
// and Node.js
let http;
let https;
let request;
const nodeJsRequest = (options) => {
return new Promise((resolve, reject) => {
let url = options.hostname;
// Use 'https' if the protocol is not specified in 'options.hostname'
if (
url.indexOf("http://") !== 0 &&
url.indexOf("https://") !== 0
) {
url = "https://" + url;
}
// Choose the right module based on the protocol in 'options.hostname'
const httpOrHttps = url.indexOf("http://") === 0 ? http : https;
// Remove the 'http://' so the native node.js module will understand
options.hostname = url.split('://')[1];
const req = httpOrHttps.request(options, (res) => {
res.on("data", (body) => {
body = body.toString();
resolve({
status: res.statusCode,
statusText: res.statusMessage,
body
});
});
});
req.on("error", (body) => {
try {
return reject({
status: res.statusCode,
statusText: res.statusMessage,
body
});
} catch (e) {
console.error(body);
}
});
if (options.body) {
req.write(JSON.stringify(options.body));
}
req.end();
});
};
const webBrowserRequest = (options) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
let contentTypeIsSet = false;
options = options || {};
const method = options.method || "GET";
let url = options.hostname;
url += typeof options.path === "string" ? options.path : "";
if (typeof url !== "string") {
return reject("HTTP Request: Invalid URL.");
}
// Use 'https' if the protocol is not specified in 'options.hostname'
if (
url.indexOf("http://") !== 0 &&
url.indexOf("https://") !== 0
) {
url = "https://" + url;
}
xhr.open(method, url);
for (let header in options.headers) {
if ({}.hasOwnProperty.call(options.headers, header)) {
let lcHeader = header.toLowerCase();
contentTypeIsSet = lcHeader === "content-type" ? true : contentTypeIsSet;
xhr.setRequestHeader(header, options.headers[header]);
}
}
if (!contentTypeIsSet) {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
xhr.onload = function() {
let body;
if (xhr.status >= 100 && xhr.status < 400) {
try {
JSON.parse(xhr.response);
body = xhr.response;
} catch (e) {
body = xhr.statusText;
}
return resolve({
status: xhr.status,
statusText: xhr.statusText,
body
});
} else {
return reject({
status: xhr.status,
statusText: xhr.statusText
});
}
};
if (method !== "GET") {
xhr.send(JSON.stringify(options.body));
} else {
xhr.send();
}
});
};
try {
window;
request = webBrowserRequest;
} catch (e) {
http = require("http");
https = require("https");
request = nodeJsRequest;
}
module.exports = request;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment