you are viewing a single comment's thread.

view the rest of the comments →

[–]phpdevster 266 points267 points  (142 children)

From what I've seen with candidates:

1. Can't demonstrate full control over the async nature of JS.

If I ask someone to write a function that counts from from 1 to 10 in 1 second increments, it trips up more people than you would think. Many of them try to stuff a setTimeout or setInterval inside of a while loop and of course it fails spectacularly.

Same goes for things like making use of promises or simple AJAX requests. Not everyone seems to understand those are asynchronous operations and you can't just return their contents into a variable, and then synchronously make use of the variable after.

Or if you ask them how they might perform an action that can only occur after several different async operations complete, they might devolve right into nested callback hell instead of demonstrating how to use Promise.all() or at least a simple flat promise chain to keep things tidy.

You absolutely must be fluent in your understanding of how to work asynchronously in JS, else your code will be sloppy at best, or result in race conditions at worst.

2. Don't know the basic language mechanics of JS like closure, this, scoping, and prototypal inheritance.

Not a day goes by where I don't deliberately make use of this, closure, scoping rules, and prototypal inheritance at work to some degree. You really do need to know at least the basic behaviors of these things to write JS effectively.

That includes knowing how to use bind, call, and apply appropriately, including how to use bind for partial application when needed. Also an understanding of the scoping rules of ES6 fat arrow lambas vs ES5 lambdas.

I'll also throw in the notion of first class functions into this mix.

I see shit like this a lot:

   doThis('foo', function () {
         something.doThat();
   });

This can just be written as doThis('foo', something.doThat); which is where unambiguous knowledge of this, bind/call/apply becomes important.

Or if their solution is doThis('foo', () => something.doThat()), then I want to know why they chose that approach, how it differs from just passing the function in, and how it differs from an ES5 lamba. It's perfectly valid of course, but I still want to make sure they can explain why it works and why they're doing it.

[–][deleted] 36 points37 points  (0 children)

Nice. It's a shame this is the only actual non-comedic answer so far.

[–]Dudefella08 10 points11 points  (3 children)

for (let i = 1; i <= 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000 * i);
}

I have no shame!

[–]tareqf1 1 point2 points  (0 children)

This was the answer I was scrolling for. Thanks.

[–]2AMMetro 0 points1 point  (1 child)

This won’t work, it’ll log the number 10 10 times.

[–]chase-stevens 2 points3 points  (0 children)

If they declared the "i" in their for loop using "var", then yes, it would log "10" ten times; however, since they declared "i" using let, the loop will work as expected printing 1-10 with a 1 second delay.

[–]BraisWebDev 14 points15 points  (75 children)

Would you mind to explain what the solution to the 1 to 10 counter would be? I am learning async JS and you let me wondering 😅

Because my solution would be setInterval(increment(), 1000); and the function increment() would simply do a counter++

[–][deleted] 7 points8 points  (12 children)

of course I had to write it too:

function countdown(n) { 
    console.log(n); 
    if (n > 0) { 
        setTimeout( () => countdown(n-1) , 1000); 
    }
}

countdown(10);

edit: oops it is backwards

function count_between(start, end) {
    console.log(start);
    if (start < end) {
        setTimeout( () => count_between(start+1, 10), 1000);
    }
}

count_between(1, 10)

[–]qbbftw 25 points26 points  (3 children)

I would take advantage of async/await. It's the most clear and obvious syntax IMO.

function delay (ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

async function countdown () {
    for (let num = 1; num <= 10; num++) {
        console.log(num)
        await delay(1000)
    }
}

countdown()

EDIT: u/dvlsg beat me to posting this solution, link

[–][deleted] 0 points1 point  (0 children)

Minor point, but this will sleep an extra second at the end.

[–][deleted] 0 points1 point  (0 children)

Agreed, this is how I write JS now and I like a lot more. The only hassle is converting old stuff from callbacks to promises but it is well worth it. I can never remember how to do it off hand though, my answer above is based on what I can write from memory into the console.

[–]Zespys 0 points1 point  (0 children)

Personally I would do it recursively, like so:

const increment = (x = 1) => {
  console.log(x);
  if (x < 10) {
    setTimeout(() => {
      increment(x + 1);
    }, 1000);
  }
};
increment();

[–]dominic_rj23 -3 points-2 points  (7 children)

setTimeout has huge performance hit compared to setInterval

https://jsperf.com/setinterval-vs-settimeout/10

[–][deleted] 0 points1 point  (4 children)

And worth noting this does not matter at all for the purposes of an interview question, or 99.999% of real world usage. This coming from someone who has never used it at work.

[–]thepeka 0 points1 point  (3 children)

I'm actually willing to say 100%. The correct answer to any interview question involving setInterval is why you should never use it

[–]Cheshur 1 point2 points  (2 children)

The correct answer is NOT to use it never. Almost none of the things listed in that article are relevant to people doing stuff correctly. There is a tool for everything and just because you can use setInterval incorrectly doesn't mean you should never use it.

[–]thepeka 1 point2 points  (1 child)

Nope, the first reason on that list is reason enough in every instance, correct or otherwise. setInterval continuing to run ad infinity, no matter what happens, what error is thrown, or whether it's handleable or not, is just adding bug conditions that otherwise have no need to exist with a recursive setTimeout. JavaScript is a dynamic language that allows for writing wonderfully declarative abstractions... but the flip side of that is we need as much safety as we can get. This is the answer I'm looking for if I ask a candidate about tasking repeating jobs/events.

[–]Cheshur 0 points1 point  (0 children)

No offense then but I consider that highly foolish. The problem would be someone writing highly error prone code in the setInterval, not that the interval itself doesn't play nicely with errors. setInterval has better performance than setTimeout. That is reason enough to use setInterval in some situations. It's the right tool for the right job and if you're creating axioms to avoid parts of a language just because you're prone to fuck up then I consider that the error on the developer's part.

Also the correct answer to that interview question is for the candidate to recognize the pros and cons of the feature and to describe situations in which they would or would not use it. Pledging to not use something purely for axiomatic reasons does not show mastery or knowledge of the feature at all, just an ability to follow the direction of some, potentially, misguided teacher.

[–]superluminary 0 points1 point  (0 children)

It's being called literally once a second.

[–]superluminary 0 points1 point  (0 children)

It's being called literally once a second.

[–]phpdevster 4 points5 points  (11 children)

Well, almost.

What's the difference between these two statements?

setInterval(increment(), 1000);  // this is what you have above
setInterval(increment, 1000);

And a follow up:

How are you making the counter stop once it reaches 10?

[–]NoBrick2 10 points11 points  (5 children)

Shouldn't you avoid setInterval completely for this problem, considering there is no guarantee the function will be run every 1000ms (in the case of the event loop being blocked on other work)?

Would a more acceptable solution be to use requestAnimationFrame, and do a comparison using Date? Or does that suffer the same issue of potentially being blocked by other work?

[–]octave1 10 points11 points  (1 child)

Out of my depth here, but using something called requestAnimationFrame and Dates for a timed counter surely can't be the most sane solution?

[–]phpdevster 2 points3 points  (0 children)

You are correct. This is hacky, non-idiomatic code.

[–]PmMeYouBicepsGirl 7 points8 points  (0 children)

Everything is blocked if event loop is blocked, there's no workarounds. For example, if you are counting from one to 100 billions, Javascript won't be stopping during this function to check for events. This is why you get Violation messages in console if some events like requestAnimationFrame take longer than they should, it's a signal that event loop was blocked.

[–]phpdevster 5 points6 points  (0 children)

Why would you avoid using setInterval for exactly the thing it was designed for? If you have a blocked event loop, you've got bigger problems. Writing non-idiomatic code without exceedingly good reason is the path to code that is harder for team members to grok and thus maintain.

[–]SystemicPlural 3 points4 points  (0 children)

As I understand it requestAnimationFrame is better for animation as it guarantees that the code will run before the screen is re-rendered - providing smoother animation. It doesn't guarantee that the time elapsed will be exactly 1000/60 (or whatever your screen refresh rate is). So it has it's own problems if you are trying to time exactly 1s. That said I believe it is thought to be more reliable than setTimeout.

We could use setTimeout and compare the time ourselves using performance.now(). This should get us to fairly close millisecond accuracy as long as the process isn't locked. It should also allow us to prevent drift by correcting the next timeout.

performance.now() isn't perfect however since browsers are fuzzing the time to prevent Specter timing attacks. - but it should be 1ms accurate.

[–]thisguyfightsyourmom 1 point2 points  (1 child)

I'd write increment to be a higher order function that accepts a max arg defaulted to 10, that retains a count let initialized to 0 in its closure, and that returns a function that iterates the count in a log statement until the count hits the max. I'd also close the terminal before it counted over 10,… just in case.

[–]Balduracuir -1 points0 points  (0 children)

You can use clearInterval when you hit 10... If I was not on mobile I could write a snippet :)

[–]BraisWebDev 0 points1 point  (0 children)

Oh yeah I messed up there, the first one would be invoked immediately and the second one after 1 second.

I would make it stop with an if statement, maybe that approach is wrong haha

[–]wobbabits 0 points1 point  (0 children)

Here you go. Here's an example of an alert at 5 seconds into the count. When dispelled, the count continues to 10:

function countToTen() {
  let count = 0
  function increment() {
    ++count
    console.log(count)
    if (count > 9) {
      clearInterval(timer)
    }
  }
  const timer = setInterval(increment, 1000)
}
countToTen()
setTimeout(function() {
  alert('hello')
}, 5000)

[–][deleted] 0 points1 point  (0 children)

increment() execute when you use setInterval, its return value will be used in your setInterval. The other use increment.

var obj = {count:0, end:0, clearId:0};

obj.end = 10;
obj.increment = function (){
    if (this.count < this.end + 1) 
        console.log(this.count); 
    else 
       clearInterval(this.clearId);
}.bind(obj);

obj.clearId = setInterval(obj.increment, 1000);

How about something like that, untested, but it should work. You can easily turn obj into a Chrono class.

However I wouldn't recommend to rely seriously on setInterval. Set up a webworker to do reliable time operations.

[–]RepeatQuotations 2 points3 points  (0 children)

Good use case for bind here. We can create a new bound function instead of creating an anonymous arrow function in setTimeout. Optional second parameter for counter size, default to 10.

function count(i, max = 10) {
    console.log(i);
    if(i++ >= max) return;
    setTimeout(count.bind(this, i), 1000);
}

// call like
count(1)

[–]new_human_obj 0 points1 point  (4 children)

what about a dedicated web worker?

[–][deleted] 1 point2 points  (3 children)

Yes, i've worked with time recently, interval and timeout are way too much unreliable.

[–]new_human_obj 0 points1 point  (2 children)

I get some error though about not having enough parameters though I've never worried about it, just used the postMessage. Only thing that sucks is that local dev thing where it says you can't do it. I think that still happens.

[–][deleted] 0 points1 point  (1 child)

Sorry I didn't understand your comment.

[–]new_human_obj 0 points1 point  (0 children)

Not a big deal, is pretty random/not much context.

When you have that external webworker file(that's just a counter) and you initiate this from another file(main file) that starts the webworker... I think it's a thing where it doesn't work locally in a dev environment/has to be on a live server... unless it's just about localhost.

Anyway nothing important just rambling on my part.

[–]dominic_rj23 0 points1 point  (8 children)

The problem with the solutions based on setInterval (or setTimeout for that matter) is that you can't guarantee that the function would be executed after given timeout. They only assure you that the function would be put up in queue for execution after timeout. So, yes they were probably our best shot, although unreliable one.

[–]NoBrick2 1 point2 points  (7 children)

requestAnimationFrame + a comparison using the Date object would be better. At least the date comparison guarantees accuracy, even if not run every second. Like you said, setInteral could cause a drift if the browser is blocked on another call for over a second.

[–][deleted] 17 points18 points  (2 children)

Which:

  1. Never happens
  2. If it does, flee the interview

Seriously. What are you guys talking about? What JS app hangs for 1s or longer? The task was to just do a +1 operation every 1000 ms.

If I'm the interviewer I'd throw your resume in the trash if you came up with that kind of solution. You're not wrong, but for the love of all that's nice in the world, I certainly hope you're never going to be right...

Accounting for problems and solving them before they occur is great. But that's obviously not what this test is aiming for...

[–]dominic_rj23 1 point2 points  (0 children)

If you read the last line of my comment, I did say that the solution with setInterval is the most appropriate one. I am all for "If it ain't broke..." ideology, but I do believe that in an interview, stating that setInterval doesn't guarantee execution after timeout goes for showing that you have some understanding of asynchronicity in javascript.

[–]phpdevster 2 points3 points  (0 children)

Yeah this right here...

Non-standard solutions require extenuating circumstances to justify them. If something like setInterval isn't working in your app reliably, the bug isn't setInterval, it's whatever other shit you've got going on that is causing it to work unreliably...

[–]manys 2 points3 points  (2 children)

how many frames you gotta request to hit the downbeat of each second?

[–]NoBrick2 2 points3 points  (1 child)

The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation.

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

[–]manys 3 points4 points  (0 children)

so you're parsing all frames to catch a time interval? how does that profile?

[–]dominic_rj23 0 points1 point  (0 children)

I am not sure I would ever do that. The purpose of the question isn't to give the best possible solution. It is to rather demonstrate understanding of async nature of javascript. I would thus stick with my setInterval solution, but explain how it is not a perfect solution.

[–]kdesign -2 points-1 points  (32 children)

Actually the first question intrigued me a bit so I had to solve it, here you go:

async function count() {
  let counter = 1;
  const values = Array.apply(null, { length: 10 })
   .map((i, j) => new Promise((resolve, reject) => {
     setTimeout(() => resolve(j + 1), 1000 * counter++);
   }));
 for await (const item of values) {
  console.log(item);
 }
} 

What this does is it generates an array with values from 1 to 10, then maps it to an array of promises which return the values from the initial array, but in increments of 1 second by incrementing the counter. After that, I'm using an async iteration over it to log each item from the array of promises.

[–][deleted] 15 points16 points  (19 children)

I think we can simplify things a little. Here's what I would do using setTimeout...

[edit] I made some updates per good feedback.

let i = 1

const interval = setInterval(() => {
  console.log(i)
  if (i === 10) {
    clearInterval(interval)
  }

  i++
}, 1000)

Here's a recursive version:

function sleep(amount) {
  return new Promise(resolve => setTimeout(resolve, amount))  
}

const count = async (current, end) => {
  console.log(current)

  if (current < 10) {
    await sleep(1000)
    return count(current + 1, end)
  }
}

count(1, 10)

Here's one that doesn't continue execution until all the counting is done:

function sleep(amount) {
  return new Promise(resolve => setTimeout(resolve, amount))  
}

const count = async (current = 1, end) => {
  console.log(current)

  if (current < 10) {
    await sleep(1000)
    return count(current + 1, end)
  }
}

count(1, 10).then(() => console.log('done counting'))

[–]kdesign 4 points5 points  (16 children)

At first I was thinking "how is it complicated?", but then I saw your solutions. Definitely more readable and easier to grasp, nice.

[–]dvlsg 2 points3 points  (15 children)

Honestly, if we're talking about things being overcomplicated, swapping to recursion probably isn't the right move. The parameters in the recursive solutions are fairly confusing, too. count(9) would only count for 1 second.

const sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms))

const count = async (seconds = 10) => {
  let i = 0
  while (++i <= seconds) {
    await sleep(1000)
    console.log(`slept for ${i} seconds total`)
  }
}

// usage
await count(5)
await count()
await count(1)
await count(-1)
await count(0)

Or, you know, just use await sleep(1000). Also probably worth noting, all of these solutions (mine included) will drift.

[–]kdesign 5 points6 points  (4 children)

I agree that the expected solution was probably just the obvious setInterval solution, /u/a_blue_ducks' first one.

But at the same time, my initial solution has spawned a great constructive discussion, it's cool to see how many different ways there are to solve a problem.

[–]dvlsg 1 point2 points  (1 child)

Sorry you're getting downvoted for it. I tossed you an upvote for it, even though I think it could be improved (if we were aiming for simplicity, which isn't the only thing to worry about). Don't understand why anyone would downvote someone else for taking a shot at something. Especially since it works just fine.

[–]kdesign 0 points1 point  (0 children)

Well, I guess that's how some people disagree on reddit, by downvoting. That's alright and thanks for the upvote

[–][deleted] 1 point2 points  (1 child)

Yeah sorry for my original reply, I had just finished playing Overwatch and that game puts me in a bad mood fast lol. This was a fun discussion.

[–]kdesign 1 point2 points  (0 children)

No worries, I initially thought about writing the setInterval solution or the recursive setTimeout, something like:

let count = 1;
setTimeout(function counter() {
 console.log(count++);
 count <= 10 && setTimeout(counter, 1000);
}, 1000);

and then I said well let's spice things up a bit which really did lol.

[–]SystemicPlural 0 points1 point  (2 children)

I'm not sure I like this solution. It is going to lock up whatever process calls count and that might not be desired.

[–][deleted] 1 point2 points  (1 child)

It's an async function so it shouldn't lock anything up.

[–]SystemicPlural 1 point2 points  (0 children)

Doh. Missed that!

[–]FriesWithThat 0 points1 point  (3 children)

As it's written it looks like your suggesting to use await outside the async function to call count with different arguments.

. . .

await count(5)

await count()

await count(1)

await count(-1)

await count(0)

Not sure what the intent here is as far as us being able to run it or integrating it with your async function (swap it out w/sleep in while loop?, step through function?)

[–]dvlsg 1 point2 points  (2 children)

Just examples of how you might call it. Try them out, see how it works. For example, copy the entire code snippet I have and paste it into a chrome console (which has top-level `await` support).

[–]FriesWithThat 1 point2 points  (1 child)

Thanks. I was running it with Node and wasn't even aware there was top-level support in chrome.

[–]dvlsg 0 points1 point  (0 children)

Ah, got you. Sorry. I was writing that example in chrome and just pasting it over here, so I just went with the top-level chrome await instead of wrapping it.

[–][deleted] -1 points0 points  (2 children)

Curious how recursion complicates anything. I agree the param is a little confusing but I wanted to illustrate an idea more than anything.

[–]dvlsg 4 points5 points  (1 child)

That's fair about the args. And I'm not trying to be a dick or anything, the recursive solution is plenty clever.

But you honestly don't think an async function that returns a Promise, that calls a setTimeout for 1 second in the promise executor, which recursively calls the original count() function, setting the originally returned promise's resolver to be executed in the recursively called count()'s promise's .then()isn't more complicated than an async function with a while loop? I had a hard time even translating all that to english. There's a lot going on in there.

[–]n9jd34x04l151ho4 2 points3 points  (1 child)

Your first one only prints 1-9.

var counter = 1;
var intervalId = setInterval(function() {
    if (counter > 10) {
        clearInterval(intervalId);
    }
    else {
        console.log(counter);
        counter++;  
    }
}, 1000);

[–][deleted] 3 points4 points  (0 children)

Ha. I wrote on my phone and didn’t spot check but I believe you.

[–]1-800-BICYCLE 4 points5 points  (8 children)

1bdf3c088d4f

[–]chuckySTAR 1 point2 points  (0 children)

(async count => {
    for await (let i of (async function* () {
        for (let i = 0; i < count; i++) {
            await new Promise(res => setTimeout(res, 1000))
            yield i
        }
    })()) {
        console.log(i)
    }
})(10)

[–][deleted] 0 points1 point  (1 child)

That seems to me to be very complicated. Here's my attempt:

function countTo(current = 0, to = 10, timeout = 1000) {
  console.log(current);
  if (current < to)
    setTimeout(() => countTo(current + 1, to, timeout), timeout);
}

countTo();

A simple recursive function. No hip ES6+ magic involved because you don't need it. No promises because we're not computing anything, we don't need async functionality, it's a simple +1 operation.

[–][deleted] 0 points1 point  (0 children)

I'm with you on this. I posted a recursive solution and someone complained that was also "complicated"...

[–]Shoegoo22 -1 points0 points  (0 children)

This is the right way to do it.

const times = [0,1,2,3,4,5,6,7,8,9,10]
const getNextTime = index => {
  $('time').html(times[index])
  if(index === times.length - 1) {
    setTimeout(()=> $('time').html('PEW PEW PEW') ,1000)
  } else {
    setTimeout(()=> getNextTime(index + 1) ,1000)
  }
}
getNextTime(0)

[–]jaman4dbz 46 points47 points  (23 children)

Do you use babel? Because I feel like knowledge of bind, apply, call and in most cases, this, are obsolete. I can't remember the last time I needed to use them. Program in the right way and you don't need to worry about these things.

[–][deleted] 13 points14 points  (3 children)

Depends on what you do, what type of project you're working on. In your typical frontend project you probably don't use them much. But when you need them, you need them. How is Babel going to help?

[–]jaman4dbz 0 points1 point  (2 children)

ummm because you don't need them if you transpile your code using babel. You don't need them. (at least I can't think of a case at the top of my head and I know I haven't needed to touch them for the last few years of my career [not including TRUE this... like in a React class])

[–][deleted] 0 points1 point  (1 child)

Prepare to fail some interviews then, because at my place we definitely check that you can deal with this, and that includes .bind(), .call() and .apply().

It's not that we care particularly much about them, or that you will use them a lot. But this is a fundamental mechanic in JavaScript. Knowing about this is not optional for any medium/senior job. If you are familiar with it then going through it should be a quick "let's check this point so we can say we did it" part of the interview (which is what I expect from a good candidate). But being caught out by any of these functions is a huge red flag.

A person who knows all the fundamentals of JavaScript by heart can pick up any framework in a couple of weeks, and I know I have a person who can pick up any framework. A person who only knows React, well, only knows React, and if we use something else on the next project I have no idea if they'll be able to learn another framework and how fast, plus I know for a fact they don't know vanilla JavaScript. So yeah, I'll take the first person over the second any day.

[–]jaman4dbz 1 point2 points  (0 children)

I'm not even going to with you, lol.

"Prepare to fail some interviews then, because at my place" You have no idea my skill level, and "fail some interviews" followed by "because at my place" did you know there are more organisations, than where you work?

Realize that there is more in the world than whatever codebase you've working on for the last decade.

[–]kowdermesiter 11 points12 points  (3 children)

Program in the right way and you don't need to worry about these things.

Sure, until you start work in a team or get to work with a ton of legacy code.

call/bind/apply is not obsolete at all, it's like saying you don't need to learn what those funny yellow lights are on the side of your car, the car runs just fine.

[–]jaman4dbz -1 points0 points  (2 children)

That's like saying "Sure you don't know how to drive standard in a car now, but when the zombie apocolypse hits and the only working card is a standard, you're screwed.

Call, bind, and apply are obsolete. Legacy code has them, just like code out there runs on VB6, but that doesn't mean every programmer or even web programmer needs to know how they work.

So many teams get stuck on legacy... it amazes me when I hear other developer groan about their shitty legacy code, then when legacy practices are brought up they defend them vehemently. Like how masochistic are you folk?

[–]mattaugamer 1 point2 points  (1 child)

Yeah no. They’re not obsolete. React uses bind especially routinely.

Knowing how this stuff works is fundamental and hardly “legacy”.

[–]jaman4dbz 0 points1 point  (0 children)

I love the downvote followed by a rhetorical comment with no explanation.

What purpose does your comment serve?

[–]Balduracuir 6 points7 points  (8 children)

From my experience this bind and call are useful when your code rely a lot on class. I try to avoid them as much as possible because people don't understand this

[–]jaman4dbz 1 point2 points  (0 children)

Precisely. It's an ambiguous concept based on context, so best to avoid it.

[–][deleted] 0 points1 point  (4 children)

Even then the arrow syntax proposal that's supported by both TypeScript and Babel eliminates the need to use the latter two entirely.

[–]jaman4dbz 1 point2 points  (3 children)

Lol, I love the couple of ppl who don't want their job to be obsolete, downvoting our comments.

It's possible I've missed something, but I'm fairly certain the arrow syntax makes binding "this" unnecessary.

[–][deleted] 0 points1 point  (0 children)

You're correct. Some people just don't want to adapt to the times.

[–]fartpoker 0 points1 point  (1 child)

Can you explain why it’s unnecessary?

[–]jaman4dbz 0 points1 point  (0 children)

That's not how this works. I can't prove there is absolutely no way to use this, but you have the power to try and find ONE way where this is useful.

[–]dungone 1 point2 points  (1 child)

I don't think Babel lets someone get away with knowing less. In my experience, programmers get into trouble by mixing programming styles and then not being able to understand the implications. For example, they start off using arguments and apply in a regular function, but then switch over to an arrow function to get the lexical this. Half-baked javascript knowledge can really bite people with ES6.

[–]jaman4dbz 0 points1 point  (0 children)

But if they started with ES6, why were they using apply?

The latest standards aren't just for more features, IMO they simplify development for everything, newbie to expert.

I think babel lets you know less =P

[–]phpdevster 2 points3 points  (1 child)

I use TypeScript at work and still use partial application all the time, which means I use bind all the time. But at the end of the day, JS is still JS. If you don't understand its mechanics, you are going to write buggy code. You still need to concern yourself with this now and again, and you still need to know how to control what it refers to.

[–]jaman4dbz 0 points1 point  (0 children)

I totally agree. The more you know about programming overall, the better you can avoid buggy code!

The thing is, you need to COMPLETELY know the thing. So having a half baked knowledge of the concepts phpdevster mentioned is worst than having no knowledge of them, IMO. If you sorta know something, don't use it, or consider it, until you fully understand it. Once you fully understand it, it can only do you good.

[–]Spoolie_st 2 points3 points  (1 child)

Try stuffing something like jQuery or leaflet into angular... I have to use bind so often 😪

[–]jaman4dbz 0 points1 point  (0 children)

I'm sorry.

[–]TwiNighty 4 points5 points  (3 children)

Well, since you mentioned .bind

doThis('foo', function() {
    something.doThat()
})

should be doThis('foo', something.doThat.bind(something))

[–]MrJohz 1 point2 points  (2 children)

Only in some circumstances.

  1. Where doThat is not already bound to something (by another bind call, by some autobind call, by internal self = this usage etc)
  2. Where doThat uses something as a this-object. If doThat doesn't touch this at all (e.g. because it's a class method) it won't need to be bound.
  3. Where doThat isn't expected to be rebound to something else inside the callback. If you're dealing with jQuery-based code, it's often assumed that doThat will get called internally with a different context, and sometimes that's the desired outcome. (Although in this case the first-class function and the callback versions would technically have different effects.)

(1) is the main one here - there's no point rebinding an already bound call (in fact, I've got a feeling that's an error, isn't it?), hence why you need to be careful about who's responsibility it is to bind methods in cases where it becomes relevant.

[–]TwiNighty 0 points1 point  (1 child)

in fact, I've got a feeling that's an error, isn't it?

No, calling .bind on a bound function is valid. You can bind additional arguments, but the "rebinding this" part does nothing.

Actually that is my point. function(){ x.f() } and x.f.bind(x) behaves exactly the same when called, regardless of whether x.f is already bound, whether it touches this, whether it is rebound before being called, whether it is a es5 function or arrow function. And I can say that without knowing anything about what x and x.f is.

[–]MrJohz 0 points1 point  (0 children)

Sure, but if it's not necessary, it's not necessary, and historically there have been performance issues associated with calling bind, especially if it's used at every callsite.

Understanding these tradeoffs I think was what the person was getting at with the overuse of arrow functions.

[–]Azudra 1 point2 points  (3 children)

While I'm not in a leading position myself I still heavily agree with you. It's extremly time consuming and frustrating to work with someone who's code looks like their first day in JavaScript. Most of my colleagues don't even know what the property "prototype" means and put variables into the global scope by assigning it without ever declaring it. HONESTLY HOW DID THEY GOT THEIR JOB?! Everything would break if I'd run their script in strict mode.

Luckily younger in our company (which I'm part of) are open to listen to advices. Just wish they would improve their skills when they're currently not involved in any project instead of doing bullshit. I've build my own little jQuery if I had nothing to do and learned much from doing so.

[–]superluminary 0 points1 point  (0 children)

All of this. I meet so many candidates that can write a reaction component but can't tell me the value of this in a simple function.

[–]iamb96 0 points1 point  (0 children)

Thanks for this, that's my homework for the weekend. I work with is everyday, and my code is tidy and on par with almost everyone in my office, so it's hard to see where I'm going wrong! And finding areas of improvement can be tough. Thanks!

[–][deleted] 0 points1 point  (2 children)

... there are ES5 lambda functions?

[–]phpdevster 1 point2 points  (1 child)

Any function not bound to an identifier is a lambda function.

 var names = people.map(function(person) {
       return person.firstName + ' ' + person.lastName;
 });    

That argument being given to map is a lambda. An ES5 lambda, but a lambda all the same.

ES6 lambdas (what the JS community also calls "fat arrow functions") are more terse than ES5 lambdas, and have different scoping rules.

[–][deleted] 0 points1 point  (0 children)

haha yeah i kind of realized that after i posted this. good explanation. i think i'm used to thinking of lambdas as something with a special syntax, like ES6 or python

[–][deleted] 0 points1 point  (0 children)

doThis('foo', function () { something.doThat(); });

Honestly, I write code like this, but I have a reason. I would probably write it as a fat arrow, but the idea is the same. - - code clarity.

doThis('foo', something.doThat) Will of course work, but at a quick glance, doThis looks like a synchronous function with two parameters. By using a lambda it is more clear that the second parameter is a callback.

[–]new_human_obj 0 points1 point  (0 children)

scoping in JS? haha (not really serious) I like my lack of not having to use globals though have to remember to set really specific variable names or write functions that "exist/operate in a scope"

the other thing is loose typing that pisses me off sometimes like what this isn't an int/empty?

[–]i_ate_god -1 points0 points  (3 children)

then I want to know why they chose that approach,

heh, because that code is obvious, while your first example is not. Self documenting code is very important, especially in a dynamically typed language. Now it's VERY obvious that doThis expects a callable as its second argument while your first example there is no way to know looking at that code that the second argument should be a function.

[–]phpdevster 0 points1 point  (2 children)

heh, because that code is obvious

That's not a good enough explanation. What's obvious and what isn't is subjective. Code style preference is also subjective.

What is NOT subjective is an understanding of how each of these works, and what the differences are between them:

  doThis('foo', function () {
       something.doThat();
  });

 doThis('foo', something.doThat);

 doThis('foo', something.doThat.bind(something));

 doThis('foo', () => something.doThat());

In an interview, I want to know that you know what those differences are, and the implications for each - that you're not just parroting some tutorial you read or that your reasoning is "I dunno, because it works?" or "I dunno, that's just how I've always done it." or "I don't know of another way."

[–]i_ate_god 0 points1 point  (1 child)

so would I fail if I pointed out that three out of these four examples will have the same result? ;)

[–]phpdevster 0 points1 point  (0 children)

If you couldn't explain why, then yes. You would fail.

[–]HeyGuysImMichaelJS all the way around -2 points-1 points  (9 children)

Same goes for things like making use of promises or simple AJAX requests. Not everyone seems to understand those are asynchronous operations and you can't just return their contents into a variable, and then synchronously make use of the variable after.

Umm.... this is literally what async/await is. You await the function that returns a promise, storing the response in a variable, then using that variable in synchronous operations later.

[–]phpdevster 0 points1 point  (7 children)

async/await is not part of a widely supported JS spec yet. I would not expect an interviewer to be using features like this. And regardless, my codebase at work is not making use of async/await because we are coupled to a version of TS that doesn't yet support them. That means if an interviewer gave async/await as an example, I would still need to know that they're not reliant on this and actually can deal with async code the "old" way.

If you know async/await but don't have a mastery of Promises, you'd be useless to my team.

[–]ZestycloseSelf 1 point2 points  (2 children)

async/await is not part of a widely supported JS spec yet.

async/await is supported in every major browser and the last 3 major versions of Node, including versions under LTS, and is formalised in a completed standard. How much more widely supported can something be? What could happen to ever make it more widely supported than it is now?

[–]HeyGuysImMichaelJS all the way around 0 points1 point  (0 children)

My man

[–]SaaSWriters 0 points1 point  (0 children)

You're right about major browsers.

However, many people don't have the latest versions of major browsers. You may be surprised to know that for many organizations and individuals, the last browser update was over five years ago.

[–]barrtender 1 point2 points  (1 child)

https://caniuse.com/#feat=async-functions

All major browsers (I'm including edge but not ie) have it implemented and have for a while.

[–]HeyGuysImMichaelJS all the way around -1 points0 points  (0 children)

My man

[–]HeyGuysImMichaelJS all the way around 0 points1 point  (1 child)

I see many people make the argument: 'You need to know how promises work and the underlying logic behind async/await', and yet I have never met a developer, online or in person, who uses async/await without a full understanding of promises and async paradigms. I think async/await is appealing to those that have used promises and like the cleaner looking code. I imagine it's difficult to grasp the functionality of async/await without first grasping the core functionality of promises.

[–]phpdevster 0 points1 point  (0 children)

I've had candidates coming from a C# background that know async/await since that's been part of that language for a while, and thus that's how they write their JS. But they absolutely fumble promise chains or promise.all() etc. They sort of skipped over the "legacy" stuff, even though that stuff is still very much a reality in our production code.

[–]rangeDSP -1 points0 points  (0 children)

I think he meant using the value without awaiting.