How to avoid this asynchronous lazy pattern?

Many times I had to write such lazy asynchronous loading in Javascript:

if (myvar != undefined){ doSomeTreatment(myvar) } else { loadMyVarAsynchronously().then(function(value){ myvar = value doSomeTreatment(myvar) }) } 

Here myvar will be a hash attribute, not a local variable. loadMyVarAsynchronously loads asynchronously the value for myvar (with, for example, a Promise or JQuery Deferred )

Is there a pattern to avoid having to double the next line in this code?

 doSomeTreatment(myvar) 
+6
source share
4 answers

It is very difficult to talk about what you are doing here, without a concrete, real example. This is definitely not a template that I often use in my code, so I have a suspicion that you can fix it by overestimating your assumptions.

By saying, you can use the promise chain. Note that in this case, promThatLoadsVar is a promise, it is not a function that returns a promise. This means that it maintains state, and the second time this code runs forward, it immediately returns.

 promisethatLoadsVar.then(function(value) { myvar = value; doSomeTreatment(myvar); }) 

If you could give a clearer question, I would be happy to provide you with a clearer answer.

Change I want to elaborate on the example presented in the comments. Here is a solution using the LoDash library. (BTW, you should use lodash or underscore, they are basically the standard javascript library).

 var getData = _.once(function() { return $.getJSON(...) }); $('button').on('click', function() { getData().then(function(data) { showDialog(data) }) }) 

Note that getData is a wrapped function that, after the first call, will return the same thing over and over without calling the function again. On the first call, it returns a promise that is resolved when the data is retrieved. The second time you return the same promises that are likely to be resolved.

Do you want to pass getData parameters?

 var getData = _.memoize(function(id) { return $.getJSON(url+id) }); $('button').on('click', function() { getData($('#selector').val()).then(function(data) { showDialog(data) }) }) 

This will do the same, but cache promises with the first input parameter passed to getData .

+1
source

If myvar already defined, you can pass this to $.when() and automatically include it in the resolved promise:

 var def = (myVar !== undefined) ? myVar : loadMyVarAsynchronously(); $.when(def).then(doSomeTreatment); 

Make the myvar assignment inside doSomeTreatment() , if required, instead of an anonymous internal .then callback, this allows you to pass doSomeTreatment directly as a function reference.

EDIT , do you need standard Promises or jQuery promises? The above jQuery style.

+2
source

Yes, you must build a promise for value. Then you just need to attach this function only once as a handler:

 var loadedMyVar = myvar!=undefined ? Promise.resolve(myvar) : loadMyVarAsynchronously(); loadedMyVar.then(doSomeTreatment); 

Oh, and you don't need to reassign asynchronously to myvar , just use the loadedMyVar promise everywhere. It’s even possible to use

 var loadedMyVar = loadedMyVar || loadMyVarAsynchronously(); loadedMyVar.then(function(value) { return myvar = value; }).then(doSomeTreatment); 
+1
source

The problem is using myvar != undefined as a check to see if it should be retrieved, which means that a search may already be in myvar != undefined when you initiate another. You have completed the request twice (or more!).

To avoid this, you can keep the promise by considering it as the future value for myvar , rather than using myvar at all:

 // assume myvarpromise is persisted somewhere longer than the // life of this function myvarpromise = myvarpromise || loadMyVarAsynchronously(); myvarpromise.then(doSomeTreatment); 
+1
source

Source: https://habr.com/ru/post/981675/


All Articles