问题
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.
回答1:
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".
回答2:
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
- It memoize Promise-based function, except errors by default
- You can set expiration time for result
- If you need, you can remember (and set expiration time) for errors too.
- 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));
回答3:
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.
回答4:
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.);
});
回答5:
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 ;)
来源:https://stackoverflow.com/questions/28763057/memoization-of-promise-based-function