How to implement dependency between asynchronous functions in JavaScript?

In a simplified case, I have two asynchronous functions, foo and bar . bar requires the result foo , i.e. bar depends on foo . I have no idea which function will be called first.

  • If bar is called first, bar calls foo and starts immediately after foo completes.
  • If foo is called first and last, bar can use the result of foo .
  • If foo is called first and bar is called before foo completes, bar must wait for the result of foo . (Do not make a new call to foo , just wait for the call already running on foo )

How can i achieve this?
Is it possible to register the dependency chain of an asynchronous function (something like a dependency in require.js define['foo'], function() { bar(); } )?
Can I use $.deferred() to achieve it?
How?

+4
source share
5 answers

In such circumstances, the standard approach is to cache a lower-level promise.

Typically, you install a js plain object in some suitable external area as a promise cache and always look there first before calling your async process.

 var promiseCache = {}; function foo() { if(!promiseCache.foo) { promiseCache.foo = doSomethingAsync(); } return promiseCache.foo; } function bar() { return foo().then(doSomethingElseAsync); } 

Of course, there is nothing that would prevent you from caching a promise of a higher level, if necessary.

 function bar() { if(!promiseCache.bar) { promiseCache.bar = foo().then(doSomethingElseAsync); } return promiseCache.bar; } 

EDIT: forceRefresh function

You can force a function to update its cached promise by passing a (optional) parameter.

 function foo(any, number, of, other, arguments, forceRefresh) { if(forceRefresh || !promiseCache.foo) { promiseCache.foo = doSomethingAsync(); } return promiseCache.foo; } 

Making forceRefresh last argument, leaving it the same as passing false , and foo will use the promise with the cache, if available. Alternatively, go doSomethingAsync() true to ensure that doSomethingAsync() is called and the cached value is updated.

EDIT 2: setName () / getName ()

When using the forceRefresh mechanism in getName() :

 setName(newName).then(getName.bind(null, true)); //set new name then read it back using forceRefresh. 

Alternatively, omit the forceRefresh mechanism and, assuming the cache property is promiseCache.name :

 setName(newName).then(function() { promiseCache.name = $.when(newName);//update the cache with a simulated `getName()` promise. }); 

The first method is more elegant, the second is more effective.

+2
source

You can just think that both functions are independent. This way, you are not running dependencies that are mutually dependent that work asynchronously. Then you can use another module that uses them.

Since they do asynchronous things, consider using promises. You can use deferred jQuery for compatibility. Think of postponement as read / write, and promises are read-only.

 // foo.js define(function(){ return function(){ return new Promise(function(resolve, reject){ // Do async stuff. Call resolve/reject accordingly }); }; }); // bar.js define(function(){ return function(){ return new Promise(function(resolve, reject){ // Do async stuff. Call resolve/reject accordingly }); }; }); // Your code (Excuse the CommonJS format. Personal preference) define(function(require){ // Require both functions var foo = require('foo'); var bar = require('bar'); // Use them foo(...).then(function(response){ return bar(); }).then(function(){ // all done });; }); 
0
source

Try creating an object property with possible values undefined , "pending" , true ; calling deferred.resolve() when obj.active is true , deferred.reject() when obj.active "waiting"

 var res = { active: void 0 }; var foo = function foo(state) { var t; var deferred = function(type) { return $.Deferred(function(dfd) { if (res.active === "pending" || state && state === "pending") { res.active = "pending"; dfd.rejectWith(res, [res.active]) } else { res.active = state || "pending"; t = setInterval(function() { console.log(res.active) }, 100); setTimeout(function() { clearInterval(t) res.active = true; dfd.resolveWith(res, [res.active]) }, 3000); } return dfd.promise() }) .then(function(state) { console.log("foo value", state); return state }, function(err) { console.log("foo status", err) return err }) } return deferred() } var bar = function bar(result) { var deferred = function(type) { return $.Deferred(function(dfd) { if (result && result === true) { setTimeout(function() { dfd.resolveWith(result, [true]) }, 1500) } else { dfd.rejectWith(res, [res.active || "pending"]) }; return dfd.promise() }) } return deferred().then(function(data) { console.log("bar value", data); }, function(err) { console.log("bar status", err); }) } $("button").click(function() { $(this).is(":first") ? foo().then(bar, bar) : bar(res.active === true ? res.active : "pending") .then(foo, foo).then(bar, bar) }) 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> </script> <button>foo</button> <button>bar</button> 
0
source

Not sure I understood the question correctly. But here is my example:

  • Put the function foo in the variable

    var foo_fn = function foo(foo_args){// Your async code goes here}

  • foo is async and returns something at some point. In your definition of foo, I recommend using promises, a concept designed to manage the composition of asynchronous functions in a clean and scalable way. Implementing the jQuery concept is convenient in many simple use cases, but it has some disadvantages that force you to use one of the many promises libraries that follow the Promises / A specification at some point. For more information, you can refer to: Wed https://thewayofcode.wordpress.com/2013/01/22/javascript-promises-and-why-jquery-implementation-is-broken/ and https: //blog.domenic .me / youre-missing-the-point-of-promises

  • so, say, foo takes arguments and returns a promise, which is later resolved to some value.

    var foo_fn = function foo(foo_args) { return foo_fn.promise = new RSVP.Promise (resolve, reject) {
    // Your async code goes here } }

    Here I use the RSVP promise library, but any promise library following the Promises / A specification can do the job.

  • When bar is called, you can simply:

    function bar (bar_args) { var foo_promise = foo_fn.promise; // if foo was called, whether the computation is in progress or finished, // the foo_fn.promise field will be non-empty, as foo returns immediately // with a promise anytime it is called `` if (!foo.promise) { // foo has not yet been called so call it foo_promise = foo(foo_args); } foo_promise.then (function (foo_result) {/*some async code here*/}) }

NOTE. This solution is very similar to the solution proposed by Roamer-1888 . One difference is that in the Roamer clause, the foo function will always return the same value after performing its asynchronous calculation. I do not know if this is the intended behavior. In my implementation, foo does async. calculation every time it is called. bar will use the last calculated value, which is stored in the foo_fn.promise field. Old calculations are lost, possible calculations are not taken into account.

0
source

If you intend to use this template, often used in your code, you can also create a function that works on the define function model in require.js .

You will need:

  • registry for storing dependency functions ( foo in your example)

  • the dependent function ( bar in your example) will have to take the calculated values ​​of the dependency functions as part of their signature. For example, a dependency hash can be passed as the first parameter, so the bar signature could be: {foo: foo_result}, other_bar_args...

  • the dependency function should follow the model of my previous answer, that is, register their promise value as a property for themselves when they are fulfilled.

  • Reminder: you need to name these dependency functions in order to refer to them inside your body, and then add this object to the registry.

In the body of the define function, you transfer the dependent function to another, which:

  • Get all dependencies from the registry

  • Get all the dependency values ​​by running the dependencies if necessary (similar to my previous answer). This means that you have a list of promises, the results of which are then gathered together ( RSVP.hash , for example, with the RSVP promise library). I believe jQuery has a similar function with jQuery.when

  • you call a dependent function ( bar ) with this hash of results as the first argument, the other arguments match the wrapped function

  • this completed function is the new bar , so when bar is called, it will be a certified function that will be called.

A little long, but it should work. If you want to see some kind of code, let me know if this is what you were looking for. Anyway, if you have a complex asynchronous one. you might be interested in using a compatible promise library in your code. $ .deferred should also be used only when you don’t have anything better, because it makes it difficult to track the behavior of your functions: you need to keep track of all the places where this pending seems to be speculating about your program.

0
source

All Articles