From: 송정기 [mailto:[email protected]] 

> Promise already abstracts that async operation. AsyncMap is just a Map that 
> runs async. No more no less. It does get/set/... the entry exactly the same 
> way as Map does but returning Promise that resolves with the value.

That doesn't really address the issue. Let me elaborate.

Promises represent an asynchronous operation. The creator of the promise 
provides a "hook," the "executor" [1], for actually performing that async 
operation. One the executor has resolved or rejected the promise, the promise's 
value can be accessed from the outside with `then`. The result is that promises 
are created with code that looks like:

```js
var myPromise = new Promise((resolve, reject) => {
  // do async process to resolve or reject the promise
});
```

For AsyncMap, it doesn't represent an async operation. Rather, it more or less 
represents an entire database API. To have a useful AsyncMap class, you would 
need to have the creator of the AsyncMap instance define:

- What async process does `get` do?
- What async process does `has` do? (Cannot be derived from `get`, because 
`undefined` is a valid value to store in a map) 
- What async process does `set` do?
- What async process does `delete` do?
- What async process does `forEach` do?

From there you could derive, somewhat inefficiently, clear, entries, keys, and 
values. But this is a lot more than the single promise executor. The only way I 
can see to make this work would be essentially:

```js
var myAsyncMap = new AsyncMap({
  get(key) { /* do async process for get */ },
  has(key) { /* ... */ },
  set(key, value) { /* ... */ },
  delete(key) { /* ... */ },
  forEach(callback, thisArg) { /* ... */ }
});
```

Now you can use that myAsyncMap instance, and e.g. calling `get` will call the 
provided hook on it, doing whatever specific async op is appropriate for that 
particular async map.

This is kind of silly, because the only value you have added is (probably 
inefficient) automatic implementations of `clear`, `entries`, `keys`, and 
`values`.

A better approach would be to just create a custom class for your specific use 
case, e.g. a `Cache` class, and have it efficiently do cache-specific I/O. That 
cache class can conform to a duck-typed interface, but there's no point in 
actually reifying that interface into an `AsyncMap` class with user-provided 
hooks.

> The "hook" should be provided by additional methods defined in the derived 
> class, when necessary. Assuming we have AsyncMap (something like the 
> following) ready as a language construct in ES,
> // Syntax is in TypeScript interface
> ...
> > AsyncMap.prototype methods are supposed to provide the generic *async* Map 
> > operations.

Part of the issue is might be that you are seeing things in terms of 
TypeScript, instead of in terms of JavaScript. In JavaScript, those generic 
type parameters are meaningless, and the entire idea of a "interface" is 
meaningless. In JavaScript, there are constructible classes, and nothing else.

The design you outline seems to be toward some sort of "abstract base class" 
idea, where there is no implementation of get/has/set/delete/forEach, but there 
is an implementation of "generic *async* Map operations", viz. 
clear/entries/keys/values. But JavaScript doesn't have abstract base classes!

> Isn't it a good idea to have generic asynchronous Map in JavaScript? It's all 
> async and we have Promise now. WDYT?

I think having a generic contract that async map-like things conform to is 
fine, e.g. what methods will it have. But I don't see much value in providing 
an actual AsyncMap constructor.


[1]: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-initializepromise

Reply via email to