Last active
September 28, 2022 16:29
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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