Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement dependency between asynchronous functions in JavaScript?

As a simplified case, I have two async functions, foo and bar. bar needs the result of foo, i.e. bar depends on foo. I have no idea about which function will be called first.

  1. If bar is invoked first, bar will call foo and start itself right after foo is done.
  2. If foo is invoked first and done, bar can use the result of foo.
  3. If foo is invoked first and bar is invoked before foo is done, bar needs to wait for foo's result. (Don't invoke a new call to foo, just wait for the already-fired call to foo)

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

like image 731
lzl124631x Avatar asked Aug 13 '15 12:08

lzl124631x


People also ask

How does JavaScript handle asynchronous code?

JavaScript provides three methods of handling asynchronous code: callbacks, which allow you to provide functions to call once the asynchronous method has finished running; promises, which allow you to chain methods together; and async/await keywords, which are just some syntactic sugar over promises.

How does JavaScript know when a function is asynchronous?

To detect if a function is asynchronous, use the function's constructor.name property: const isAsync = myFunction.constructor.name === "AsyncFunction"; If the value is AsyncFunction , you know the function is async !

How is JavaScript async await implemented?

JavaScript Async FunctionsAsync and await are built on promises. The keyword “async” accompanies the function, indicating that it returns a promise. Within this function, the await keyword is applied to the promise being returned. The await keyword ensures that the function waits for the promise to resolve.


1 Answers

In circumstances like this, the standard approach is to cache the lower level promise.

Typically you will establish, in some suitable outer scope, a js plain object 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's nothing to prevent you also caching the higher level promise, if appropriate.

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

EDIT: forceRefresh feature

You can force a function to refresh its cached promise by passing an (extra) parameter.

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

By making forceRefresh the last argument, leaving it out is the same as passing false and foo will use the cached promise if available. Alternatively, pass true to guarantee that doSomethingAsync() be called and the cached value be refreshed.

EDIT 2: setName()/getName()

With the forceRefresh mechanism in place 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 to be 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 more efficient.

like image 50
Roamer-1888 Avatar answered Nov 06 '22 22:11

Roamer-1888