Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
MrMoo
Sep 14, 2000

Roadie posted:

Use p-retry instead. It has better coverage for various edge cases including being able to abort the retries from the outside.

Probably need something with a configurable increasing delay and randomization, node-promise-retry has that.

Adbot
ADBOT LOVES YOU

TheFluff
Dec 13, 2006

FRIENDS, LISTEN TO ME
I AM A SEAGULL
OF WEALTH AND TASTE
Just use the async library for everything. It does everything you could possibly want to do with asynchronous functions and is far better than anything else I've seen. It lets you run asynchronous tasks in series, in parallel, or in parallel with a limit on concurrent tasks. It supports asynchronous iterators for all the usual suspects (map, filter, reduce, etc) as well as asynchronous control flow. async.auto is the greatest thing to happen to Javascript since the arrow function.

TheFluff fucked around with this message at 06:51 on Jun 14, 2018

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

TheFluff posted:

Just use the async library for everything. It does everything you could possibly want to do with asynchronous functions and is far better than anything else I've seen. It lets you run asynchronous tasks in series, in parallel, or in parallel with a limit on concurrent tasks. It supports asynchronous iterators for all the usual suspects (map, filter, reduce, etc) as well as asynchronous control flow. async.auto is the greatest thing to happen to Javascript since the arrow function.

Does it return the original request data in the response like axios does?

TheFluff
Dec 13, 2006

FRIENDS, LISTEN TO ME
I AM A SEAGULL
OF WEALTH AND TASTE

Grump posted:

Does it return the original request data in the response like axios does?

It returns whatever the underlying functions you pass to it return. It's a utility lib for whatever async functions you have, whether they are async/await, callback-based or promise-returning. Most promise implementations have things like Promise.all(<array of promises>), Promise.map, Promise.filter and so on. This has that and then some - composition of async functions, queues, loops and flow control, memoization, the works. async.auto is particularly noteworthy for solving pretty much all obnoxious scoping and task dependency problems you might have in a trivially simple way. It's great, go check it out.

MrMoo
Sep 14, 2000

TheFluff posted:

Just use the async library for everything. It does everything you could possibly want to do with asynchronous functions and is far better than anything else I've seen. It lets you run asynchronous tasks in series, in parallel, or in parallel with a limit on concurrent tasks. It supports asynchronous iterators for all the usual suspects (map, filter, reduce, etc) as well as asynchronous control flow. async.auto is the greatest thing to happen to Javascript since the arrow function.

Nice, but weird all the callbacks instead of using Promise chains. 2 steps forward, 2 steps back kind of thing.

SimonChris
Apr 24, 2008

The Baron's daughter is missing, and you are the man to find her. No problem. With your inexhaustible arsenal of hard-boiled similes, there is nothing you can't handle.
Grimey Drawer

TheFluff posted:

It lets you run asynchronous tasks in series...
Maybe everyone else already knew this, but running async tasks in series is actually super easy using ES7 async/await:
code:
for( let thingy of thingies ) {
  await doAsyncStuffWithThingy(thingy);
}
Note that this only works with the let ... of ... syntax. A normal for loop is not sufficient, for some reason.

I spent ages implementing weird recursive functions to pull this off, so I just wanted to throw this out there, in case other people are having the same problem :).

MrMoo
Sep 14, 2000

Although it is almost one liner normally anyway:
JavaScript code:
thingies.reduce((promise, thingy) => 
    promise.then(result => doAsyncStuffWithThingy(thingy)), Promise.resolve());

LifeLynx
Feb 27, 2001

Dang so this is like looking over his shoulder in real-time
Grimey Drawer
What am I doing wrong here:

code:
window.onscroll = function () { scrolledPast() };

function scrolledPast() {
	document.getElementById('#asttop').classList.add('scrolledpast');
}
Chrome says "Uncaught TypeError: Cannot read property 'classList' of null" but #asttop definitely exists.

TheFluff
Dec 13, 2006

FRIENDS, LISTEN TO ME
I AM A SEAGULL
OF WEALTH AND TASTE
Dumb question: is it actually, like, <div id="#asttop">? Because using # as a prefix for ID's is a CSS/jQuery thing.

If it actually is #asttop, does the element always exist or does it get created by javascript dynamically at some point?

Happy Thread
Jul 10, 2005

by Fluffdaddy
Plaster Town Cop
You can also mix es6's built in keyword called aync with Array.reduce to get a really powerful way to run a lot of Promises that all wait for each other.

code:
const fetch_promise_maker = one_request_worth_of_data =>
{  // Build your custom call to fetch
}

return all_your_data.reduce( async ( promise, one_piece_of_data ) =>
{ // put together one_request_worth_of_data 
  await promise;
  return fetch_promise_maker( one_request_worth_of_data );
}, Promise.resolve() );
That seemed to me like a better alternative to pulling in an entire library that's still confusingly called async, I guess before es6 offered that keyword and presumably not taking advantage of es6 internally.

Happy Thread fucked around with this message at 18:29 on Jun 15, 2018

Happy Thread
Jul 10, 2005

by Fluffdaddy
Plaster Town Cop
I'm always only sending JSON objects between client and server. I always just call the below instead of fetch. This way it rejects on page loading errors like 404 and 504 and you can pass in the callback of what to do if that happens.

code:
fetch_handler( url, body )          // A general utility function for sending / receiving JSON, with error handling.
  { return fetch( url, 
      { body: body, method: body === undefined ? 'GET' : 'POST', 
       headers: { 'content-type': 'application/json'  } 
      }).then( response =>
      { if ( response.ok )  return Promise.resolve( response.json() )
        else                return Promise.reject ( response.status )
      })
  }
Called like this:

quote:

this.fetch_handler( your_URL, JSON.stringify( your_optional_JSON_data ) )
.then ( response => { /* Modify your page here according to the JSON data you received back */ } )
.catch( error => { /* Modify your page here according to the error string you received back */ } )

Really easy.

Happy Thread fucked around with this message at 18:29 on Jun 15, 2018

LifeLynx
Feb 27, 2001

Dang so this is like looking over his shoulder in real-time
Grimey Drawer

TheFluff posted:

Dumb question: is it actually, like, <div id="#asttop">? Because using # as a prefix for ID's is a CSS/jQuery thing.

If it actually is #asttop, does the element always exist or does it get created by javascript dynamically at some point?

Oh you're right, the # was the issue. Now I have:

code:
window.onscroll = function () { scrolledPast() };

function scrolledPast() {
	if (window.scrollY > 0) {
		document.getElementById('asttop').classList.add('scrolledpast');
	}
	else { document.getElementById('asttop').classList.remove('scrolledpast'); }
}
...which does exactly what I want it to. It's replacing this jQuery script that I wrote a while ago:

code:
$(window).on('scroll', function() {
	var y_scroll_pos = window.pageYOffset;
	if(y_scroll_pos > 1) { $("#asttop").addClass('scrolledpast'); }
	if(y_scroll_pos < 1) { $("#asttop").removeClass('scrolledpast'); }
});
Thanks!

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

LifeLynx posted:

Oh you're right, the # was the issue. Now I have:

code:
window.onscroll = function () { scrolledPast() };


Can just be:

code:
window.onscroll = scrolledPast;

Roadie
Jun 30, 2013

LifeLynx posted:

Oh you're right, the # was the issue. Now I have:

code:
window.onscroll = function () { scrolledPast() };

function scrolledPast() {
	if (window.scrollY > 0) {
		document.getElementById('asttop').classList.add('scrolledpast');
	}
	else { document.getElementById('asttop').classList.remove('scrolledpast'); }
}
...which does exactly what I want it to.

Depending on how pathologically obsessed with minimalism you are, you can shrink that a bit:

JavaScript code:
window.onscroll = function () {
  document.getElementById('asttop')
          .classList[window.scrollY > 0 ? 'add' : 'remove']('scrolledpast');
};

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

Roadie posted:

Depending on how pathologically obsessed with minimalism you are, you can shrink that a bit:

JavaScript code:
window.onscroll = function () {
  document.getElementById('asttop')
          .classList[window.scrollY > 0 ? 'add' : 'remove']('scrolledpast');
};

I'm going the opposite route and make it less minimal. Yours is succinct and readable for the experienced dev, but since LifeLynx seems to be relatively new, I'm going to push it (maybe a little too far) towards readbility / maintainabiltiy.

JavaScript code:
window.onscroll = function () { scrolledPast() };

function scrolledPast() {
	if (window.scrollY > 0) {
		document.getElementById('asttop').classList.add('scrolledpast');
	}
	else { document.getElementById('asttop').classList.remove('scrolledpast'); }
}
So here we have a magic number (0) and two magic strings ('asstop', 'scrolledpast'), and no clear indication of what '0' represents. If I'm inheriting this code, I can probably figure that out, but why make it more difficult? Also, if I only search / replacing the first instance of either string, I broke everything should things change later, so let's protect against that as well:

JavaScript code:
// long, but descriptive! 
const toggleClassWhenElementScrollsPast = () => {
  
  // Now I know what these things mean
  // and I can easily make these function params
  const TARGET_Y_POSITION = 0;
  const ELEMENT_ID = 'asttop';
  const CLASS_NAME = 'scrolledpast';

  const _element = document.getElementById( ELEMENT_ID );
  
  // don't explode if our element does not exist;
  if( !_element ) {
    return;
  }
  
  // do your thing
  if( window.scrollY > TARGET_Y_POSITION ){
    _element.classList.add( CLASS_NAME )
  } else {
    _element.classList.remove( CLASS_NAME )
  }
}

// pretend I have imported / included lodash's debounce function
// this makes sure I can only be called once ever 100ms
const debouncedScrollHandler = _.debounce( toggleClassWhenElementScrollsPast, 100 );

// now I get much better performance and I don't kill any other scroll handlers
window.addEventListener( 'scroll',  debouncedScrollHandle)

This is a lot "more code", but it's probably easier to change and understand. Obviously I'm not trying to say anyone *should* code like this all the time, but just showing LifeLynx one of many ways of skinning that cat.

Roadie
Jun 30, 2013
The debounce part in particular is a really good point. That's always, always a good idea for stuff that will be fired a bunch of times like scroll handlers. Make sure it fires at the end of the period rather than the start to get the last position, though.

TheFluff
Dec 13, 2006

FRIENDS, LISTEN TO ME
I AM A SEAGULL
OF WEALTH AND TASTE
I'm usually a big fan of clarity of intent, but in my experience/opinion, 0 and 1 (and if you want to be generous, -1 and 2 too) aren't really sufficiently cryptic to be shunned as magic numbers that need an explanation and a symbolic name.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Roadie posted:

Depending on how pathologically obsessed with minimalism you are, you can shrink that a bit:

JavaScript code:
window.onscroll = function () {
  document.getElementById('asttop')
          .classList[window.scrollY > 0 ? 'add' : 'remove']('scrolledpast');
};

You should just use classList.toggle.

JavaScript code:
window.onscroll = function() {
    const scrolledPast = window.scrollY > 0;
    document.getElementById('asttop').classList.toggle('scrolledpast', scrolledPast);
};

Happy Thread
Jul 10, 2005

by Fluffdaddy
Plaster Town Cop
The function is simple enough to not use its own "this" context so it could just be an arrow function

JavaScript code:
window.onscroll = () => document.querySelector("#asttop").classList.toggle('scrolledpast', window.scrollY > 0 );
There, still clear enough about how it changes the CSS based on whether window.scrollY > 0, and as long as your eyes notice and understand that when you look at it, you can now spend more time looking at the rest of the screen, which now has more room for other useful code. It also reads more like a short sentence when it's all together.

edit: Not that I disagree with anyone; the above verbose code for beginners is a good idea to write out too.

Happy Thread fucked around with this message at 00:38 on Jun 16, 2018

necrotic
Aug 2, 2005
I owe my brother big time for this!

TheFluff posted:

I'm usually a big fan of clarity of intent, but in my experience/opinion, 0 and 1 (and if you want to be generous, -1 and 2 too) aren't really sufficiently cryptic to be shunned as magic numbers that need an explanation and a symbolic name.

It's not the content, but the purpose. The target could later be 200, it's still the y target.

Roadie
Jun 30, 2013

Dumb Lowtax posted:

The function is simple enough to not use its own "this" context so it could just be an arrow function

JavaScript code:
window.onscroll = d() => document.querySelector("#asttop").classList.toggle('scrolledpast', window.scrollY > 0 );
There, still clear enough about how it changes the CSS based on whether window.scrollY > 0, and as long as your eyes notice and understand that when you look at it, you can now spend more time looking at the rest of the screen, which now has more room for other useful code. It also reads more like a short sentence when it's all together.

edit: Not that I disagree with anyone; the above verbose code for beginners is a good idea to write out too.

Gotta debounce too though, as noted by Lumpy.

JavaScript code:
import debounce from 'lodash/debounce';

const REASONABLE_UPDATE_TIME_MS = 20;
const trailingDebounce = func => debounce(func, REASONABLE_UPDATE_TIME_MS, { trailing: true })

window.onscroll = trailingDebounce(() => document.querySelector("#asttop").classList.toggle('scrolledpast', window.scrollY > 0 ));

Happy Thread
Jul 10, 2005

by Fluffdaddy
Plaster Town Cop
Very nice. Is the debounce function something that would be hard to implement in a few lines of es6 without a library? I'm curious if I have any use cases for debouncing on my site.

MrMoo
Sep 14, 2000

It's about a page worth of code from what I've seen, haven't seen any super small versions so far.

You can pull in lodash-debounce on it's own. Polymer Debouncer is more comments than anything, but the majority of the work appears to be in their async module.

ES7 decorators maybe the way to go, a lot smaller implementation.

Otherwise for ES6, maybe this:
JavaScript code:
function debounce(func, wait) {
  let timeout
  return function(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

MrMoo fucked around with this message at 02:40 on Jun 16, 2018

LifeLynx
Feb 27, 2001

Dang so this is like looking over his shoulder in real-time
Grimey Drawer
I'm not a total beginner, I've been (ab)using jQuery for years so I can make sense out of what you're all saying... except for the lodash and debounce stuff. What are lodash and debounce? From context "debounce" looks like "hey don't run this script a billion times if conditions keep being true" but with this that's not a problem unless someone's somehow spinning their scroll wheel up and down, right? I guess what you're all saying is it's good practice to "debounce" anyway because not everything's going to be tied to scrolling past the top.

prisoner of waffles
May 8, 2007

Ah! well a-day! what evil looks
Had I from old and young!
Instead of the cross, the fishmech
About my neck was hung.

LifeLynx posted:

From context "debounce" looks like "hey don't run this script a billion times if conditions keep being true" but with this that's not a problem unless someone's somehow spinning their scroll wheel up and down, right?

Chiming in to say that scroll listeners can fire multiple times for a single user interaction, especially for momentum-y scrolling. If your scroll callback does some serious work you've already got some problems but generally speaking without debounce you have no control as to how often it will get invoked

necrotic
Aug 2, 2005
I owe my brother big time for this!
Debouncing a function means you limit execution of it to once every period. So if you call a function that has been wrapped with wrappedFn = debounce(fn, 100) you can call wrappedFn a thousand times a second but it will only execute ten times. The usual method is to trigger it immediately and then prevent execution for 100 milliseconds, known as a leading debounce.

The trailingDebounce presented above flips this and delays execution until the end of the period, which is very useful for scrolling hooks as you'll get the final value, not the value from 100 milliseconds ago.

MrMoo
Sep 14, 2000

The problem is apparently more serious with touch devices, you can watch the Google video for details and what they have done to work around it with "passive event listeners".

https://www.youtube.com/watch?v=65VMej8n23A

https://developers.google.com/web/updates/2016/06/passive-event-listeners

LifeLynx posted:

What are lodash and debounce?

"lodash" is a gimmick name for variably curated JavaScript library of things. I guess the name derived from underscore "_" which was hip a while back.

"debounce" is an odd name with precedence in electronics about limiting the number of events received for a particular action. A specialised case of throttling. Usually throttling fires on both the leading and trailing edge of the time window, debouncing only one edge as above from necrotic. Also,

CHRIS COYIER posted:

Throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."

Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called."
https://css-tricks.com/the-difference-between-throttling-and-debouncing/

MrMoo fucked around with this message at 15:12 on Jun 16, 2018

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
The other option here rather than debounced scroll is to use one of those new-fangled IntersectionObservers which will work for scrolling, resizing, any time the element comes into or out of frame. They're pretty powerful, too.

LifeLynx
Feb 27, 2001

Dang so this is like looking over his shoulder in real-time
Grimey Drawer

Suspicious Dish posted:

The other option here rather than debounced scroll is to use one of those new-fangled IntersectionObservers which will work for scrolling, resizing, any time the element comes into or out of frame. They're pretty powerful, too.

Going to look into this! Is it supported on all browsers?

I also want to say thanks for all the support! Didn't expect a whole mess of replies to my question. :)

necrotic
Aug 2, 2005
I owe my brother big time for this!
IE lacks support, but I think edge and the rest have it. MDN is a solid resource for JavaScript and had browser support.

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?
I also like caniuse for finding which features are supported on which browsers

"Usage relative" is especially useful

HappyHippo fucked around with this message at 23:11 on Jun 16, 2018

Roadie
Jun 30, 2013

Dumb Lowtax posted:

I'm curious if I have any use cases for debouncing on my site.

A big one is syncing with a remote API for "real-time-ish" services, auto-save functionality in online editors, etc. With debouncing that combines actions (slightly more complicated than the examples so far but generally not too bad), your low level input handling, buttons, etc, potentially all the way up to your data sync handling can all be written as if you were just always updating on demand, but you can still limit your network operations to once every X seconds.

mrbotus
Apr 7, 2009

Patron of the Pants
Totaly newbie hear self-learning until I can start a course in the fall:

So I started using brackets yesterday to try and practice what I've learned so far. But something very basic doesn't seem to work:

document.getElementById... etc.

It seems to think "document." is wrong? "ERROR: 'document' is undefined.

But when I type the same thing up in notepad it works fine... what's going on?

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

nickmeister posted:

Totaly newbie hear self-learning until I can start a course in the fall:

So I started using brackets yesterday to try and practice what I've learned so far. But something very basic doesn't seem to work:

document.getElementById... etc.

It seems to think "document." is wrong? "ERROR: 'document' is undefined.

But when I type the same thing up in notepad it works fine... what's going on?

'document'' only exists in a browser context - like, if you press f12 on a website using your browser, and type 'document.getElementById(x)', the command will work. however, if you're doing this from like a node.js command line or something similar, that object will not exist. so how are you running the js?

mrbotus
Apr 7, 2009

Patron of the Pants
I just opened up the "Brackets" program. I have an "html" file and a javascript link at the bottom, just before the </body> tag. No special js libraries or anything...

necrotic
Aug 2, 2005
I owe my brother big time for this!
If it's in the editor it's probably a linter not configured for the web, so it doesn't know what document is.

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself
FWIW, I had nothing but problems with Brackets when I used it. Some features would just stop working occasionally.

I would recommend VSCode all the way, and so would a lot of people in this thread

mrbotus
Apr 7, 2009

Patron of the Pants
Thanks, I'll try that.

Sab669
Sep 24, 2009

I'm not sure if there's a solution to this problem, but I have an issue on a screen with 15 text boxes ("numbered" A-O). They can only accept values 1-7, 9, 10, or 88. (if they enter a single digit, we need to change it to be a 2-digit value prefaced with a "0", so 2 becomes "02")

If you enter 9, 10, or 88 in a few of the fields the software needs to disable a few other fields. If those other fields have values already then we need to present a pop-up confirming they want to clear out the fields.

So there's JS that runs both on key up and on blur to do the following:

* validate the value
* check if "child controls" have a value in order to prompt user
* update background color & tab indexes

The problem I'm experiencing is that it's very easy to be faster than the JS, so you just repeatedly hit tab and type in a value that will disable the child controls you can enter in values before the pop-up actually appears and now you're able to save invalid data.

Short of adding server side checks, is there some way I can do this? Is it possible to make the browser "wait" on the JavaScript to make sure everything finishes before they can move on to the next textbox?

Adbot
ADBOT LOVES YOU

Chenghiz
Feb 14, 2007

WHITE WHALE
HOLY GRAIL

Sab669 posted:

Short of adding server side checks, is there some way I can do this? Is it possible to make the browser "wait" on the JavaScript to make sure everything finishes before they can move on to the next textbox?

If it's important that the user's input is valid, you should be checking server-side, regardless of whether there is a problem in the client-side validation. Never trust the client for validation.

When the user inputs a value, is it posted to the server automatically, or do they have to submit a form?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply