简体   繁体   中英

Memoization of promise-based function

How can I memoize a promise-based function?

Would straightforward memoization of the function suffice?

function foo() {
    return new Promise((resolve, reject) => {
      doSomethingAsync({ success: resolve, fail: reject });
    });
};

Would this suffice?

var fooMemoized = memoize(foo);

Note: this question has been updated to remove the deferred anti-pattern.

Yes, that will suffice. Promises are simple return values, which is their great benefit - in contrast to callbacks, where memoisation code would be horrible.

You only might want to make sure that the memoized promise is uncancellable, if your promise library does support some kind of cancellation. Also notice that this form of memoisation remembers rejections as well, so you can't recover from errors by "trying again".

For promises simple sync memoize will not be good, because in most of cases you will not wish to memoize errors (rejected promises).

I did a simple library for common needs: https://github.com/nodeca/promise-memoize

  1. It memoize Promise-based function, except errors by default
  2. You can set expiration time for result
  3. If you need, you can remember (and set expiration time) for errors too.
  4. Data can be prefetched prior to expire, to never leave cache in cold state.

Pseudo code:

let db = require('mongoose').createConnection('mongodb://localhost/forum');

function lastPosts(limit) {
  return db.model('Post').find()
    .limit(limit).orderBy('-_id').lean(true).exec(); // <- Promise (thenable)
}

let cachedLastPosts = require('promise-memoize')(lastPosts, { maxAge: 60000 });

// Later...
cachedLastPosts(10).then(posts => console.log(posts));

Note that your function has the deferred anti pattern and can be simplified further:

foo.value = null;
function foo(){
    if(foo.value) return foo.value;
    return (foo.value = doSomethingAsync());
}

That is, memoization is so simple in this case you don't even have to call .memoize . Also your original function suppressed errors.

As @Bergi, and @BenjaminGruenbaum have pointed out, yes memoization is fine here, but it should be pointed out that your foo function is doing nothing useful and is actually introducing bugs (see: deferred antipattern).

If all you want is to memoize the result of doSomethingAsync , then you can cut out the middle-man:

var fooMemoized = memoize(doSomethingAsync);

Or if you were actually oversimplifying and foo() is passing arguments to doSomethingAsync , then you can still reduce it to one line:

function foo() {
    return doSomethingAsync(argument1, argument2, etc.);
}
var fooMemoized = memoize(foo);

Or if you don't actually plan to use foo() , you can do:

var fooMemoized = memoize(function () {
    return doSomethingAsync(argument1, argument2, etc.);
});

Memoization and promises aren't obvious. Even worst with the new async / await syntax.

in order to get somewhint like that working:

memoize(async () => 42) 

or

const whatsTheAnswerToLifeTheUniverseAndEverything = () => 42
memoize(whatsTheAnswerToLifeTheUniverseAndEverything) 

You need a memoize function or library which supports promises and async syntax. A couple of them: - https://github.com/aboutlo/async-memo-ize (Disclosure: I did this lib) - https://github.com/medikoo/memoizee

Notice: Memoization is a cool technique however you save CPU resources at the cost of a memory consumption. You should take care how those libraries approach this issue at scale ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM