Skip to content

Instantly share code, notes, and snippets.

View dtipson's full-sized avatar

Drew dtipson

View GitHub Profile
dtipson / responsive-request-desktop-site.js
Last active February 17, 2024 20:49
How to make responsive sites better respect the "Request a desktop site" feature on modern mobile browsers.
Enable the "Request Desktop Site" functions on mobile chrome (android and iOS) allow users to see desktop layouts on responsive sites. Note that this is distinct from "opt out of mobile!" buttons built into your site: this is meant to work with the browser's native opt-in/opt-out functionality.
Since these functions work, in part, by simply spoofing the user agent to pretend to be desktop browsers, all we have to do is just remember that the browser once claimed to be android earlier in the same session and then alter the viewport tag in response to its fib.
Here's an example viewport tag <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> that we'd be setting to scaleable-yes,max scale=2. That's just an example of something that works on the site I was building: you should customize the "desktop" viewport content setting to whatever works for your site's needs. If you wanted, you could stick this code in the head just after the primary viewport tag so that the br
dtipson / spreadBatchesAsyncGen.js
Last active January 5, 2024 16:00
Spreading out a generator of arrays into a generator of items
// spreadBatchesAsyncGen :: Iterator[[...Promises]] -> AsyncGenerator[[...Promises]]
const spreadBatchesAsyncGen = async function* (iterableOfPromises) {
for (let array of iterableOfPromises) {
yield* await Promise.all(array)
dtipson / complete-pipeline-two.js
Last active December 26, 2023 15:08
An even simpler pipeline
const outputs = await pipe(
postIds,// array of numbers, which is... already an iterable
mapGen(fetchById),// generator of an array of eventually all resolved promises (requested in chunks of 3)
chunkGen(5),// generator of arrays of 5 promises, our "batch"
spreadBatchesAsyncGen,// async generator of individual resolved promises
forEachAsyncGen(renderPost)// Promise of an array of numbers
dtipson / complete-pipeline.js
Last active December 26, 2023 03:59
A pipeline version
await pipe(
postIds[Symbol.iterator](),// creates a generator from a plain array of postIds
chunkGen(5),// generator that pulls in 5 numbers and yields them out in arrays of 5
mapGen(map(fetchById)),// generator that takes in arrays of numbers & turns them into arrays of requests
spreadBatchesAsyncGen,// async generator that spreads arrays of resolved promises & emits one promise at a time
forEachAsyncGen(renderPost)// a function that consumes the generator & does something with each result
dtipson / batchTasks-spread.js
Last active December 25, 2023 19:42
Revised batchTasks generator
export async function* batchTasks(tasks = [], limit = 5, taskCallback = r => r) {
for (let i = 0; i < tasks.length; i = i + limit) {
const batch = tasks.slice(i, i + limit)
yield* await Promise.all( => task().then(taskCallback))
// Finally wrapped your head around Promises? Time to toss out all that knowledge and learn the functional alternative!
// Here's a super simple implementation of a Task "type"
const __Task = fork => ({fork})
// Absurdly simple! All we're doing is using a function that returns some unknown value, by name, in an object.
// At this point "fork" is just a cute name though: what matters is how we use it.
// What makes Task a "Task" is that is that the "fork" value here... will be a higher-order function.
// Here's a usage example, already fully functional, already nearly as powerful as Promise!
dtipson / cellular automata with quasi-comonads.js
Last active November 9, 2021 14:41
Using a comonad(ish) pattern to create cellular automata in a more functional way. Inspired by this episode of funfunfunction:
Native Arrays are not great structures for cellular automata.
Non-empty, circular, doubly-linked lists would be ideal...
but all we really need to do is write a comonadic-like interface
such that it _pretends_ that the array is circular, and can thus
pass the exfn below a sort of "local" slice of an Array as if it were circular.
So you can mostly ignore the implementation mess below
Array.prototype.extendNear = function(exfn){
const len = this.length;
So, let's play with some Semigroups and (maybe?) Monoids for combining lists in interesting ways
//This one is pretty straightforward
//Union (keep all values from both lists, but no repeats)
const Union = function(xs){
if (!(this instanceof Union)) {
return new Union(xs);
var object = {
increment(){ return ++this.value; }
But of course, even though that value is central to what the object is,
it's not really a _primitive_ value in the sense that we can directly coerce it into a number or string:
dtipson / workerFactory.js
Last active March 15, 2020 15:39
Template literals allow us to write string code suitable for workers. We can even import actual functions using toString. Blob urls allow us to turn all that content into a script for the worker to run, allowing us to create and run one with no external dependencies!
//actual function (must be a named function declaration for use w/ toString here)
function addOne(x){ return x+1 };
//the actual worker code can be written as a string
//template literals make that convienient because they support multiline
//note that the addOne function above can be used in this worker code, since we'll be importing it
const workerScript = `
self.addEventListener('message', function(e) {
var data =;
console.log('worker recieved: ',data);