Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

SebasQuiroga
SebasQuiroga

Posted on

     

Async JS Patterns using Thunks

This is the second part of the series Async JS Patterns, in the previous post I wrote about callbacks and how they help us to sort some common programming puzzles such as concurrency. In this post we will talk about a slightly different approach of callbacks, thunks.

Let’s first revise an important concept behind thunks.

Aclosure is the combination of a function enclosed with references to its surrounding state (thelexical environment).MDN

Let’s check the example provided by MDN, (I intentionally changed some comments and lines for a better understanding).

functionmakeFunc(){varname='Mozilla';functiondisplayName(){console.info(name);}returndisplayName;}varmyFunc=makeFunc();myFunc();// it executes the function displayName()// using all the private state (in this case the string)// from its parent function makeFunc()
Enter fullscreen modeExit fullscreen mode

As we can see in the previous snippet, a closure lets a function remembers the collection of states (similar to a conventional memory system) and play with them, it is very simple but applied in async tasks makes it very interesting.

Practical speaking a thunk is thereturned function in a function, like in the next code:

functionhelloWorld(){// Some private statereturnfunctionthunk(cb){// We have access to all private state from helloWorld() - closure -// And aditionally we can use it as a mechanism for delaying execution// (check the comments at the end of this example)cb()}}letth=helloWorld()th(cb)// Whenever the function helloWorld() has finished its instructions,// we pass to the return function a callback with more instructions,// resulting in a sequential flow!
Enter fullscreen modeExit fullscreen mode

The previous example is a taste of async thunks but let’s keep it simple, we will dig into async thunks in a while.

Technically speaking a thunk is a container around a collection of state, it is a function that returns an other function thus it has access to the parent private state (closure).

Let’s consider the next synchronous thunk:

functionsumThunk(a,b){returna+b}functioncommonFunction(){consta=10constb=10returnsumThunk(a,b)//This returns a thunk function that make use of the parent private state,// everytime it is called, it will always remember this state.// It acts like a container around a value(s), pretty similar to a token}consttotal=commonFunction()console.info(total)// 20
Enter fullscreen modeExit fullscreen mode

Asynchronous thunks behave pretty similar, however we pass as parameter a function(callback) so whenever the value is ready (like the fetching of some data) it executes the function (resulting as asequentially and blocking way of sorting asynchronous tasks).

Let’s code a program that fetches some data, but this time we will code it as intuitive as our brain would most likely sort it.

functionfetchAPI(rq,time){constapiResponses={'file1':'First File','file2':'Second file'}setTimeout(function(){console.info(apiResponses[rq])},time)}functionexecuter(rq,time){fetchAPI(rq,time)}letth1=executer('file1',3000)letth2=executer('file2',100)// Second file// First file
Enter fullscreen modeExit fullscreen mode

The output of the previous example is not what we initially expected, we want thefile1 to be fetched first and then thefile2. The real problem here is with JS engine, because it is responsible for executing instructions as fast as possible without worrying about waiting for slow responses, so what happens here is that as thefile1 needs 3 seconds to execute and thefile2 just 100 seconds, the JS engine will choose thefile2 execution first than thefile1, contrary to what we really want.

Now let’s consider the next example usingthunks for managing async tasks correctly (sequentially and blocking)

functionfetchAPI(rq,time,cb){constapiResponses={'file1':'First File','file2':'Second file'}setTimeout(function(){cb(apiResponses[rq])// - function ready() - lazy executed!},time)}functionexecuter(rq,time){// we return a thunk with the - function ready() - as a 'future' callbackreturnfunctionthunk(cb){fetchAPI(rq,time,cb)}}// We return the function of  - function executer() - in the variable th,// as it is a function we can pass a param(in this case a callback - function ready() ),letth1=executer('file1',3000)letth2=executer('file2',100)th1(functionready(salida){console.info(salida)// When JS engine comes in this line the first task has completed,// so now we launch the second oneth2(functionready(salida){console.info(salida)})})
Enter fullscreen modeExit fullscreen mode

References

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I am a software engineer focus on developing robust systems in the cloud and sometimes even on earth.
  • Location
    Colombia
  • Work
    SW Specialist
  • Joined

More fromSebasQuiroga

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp