2

I have 2 methods that are almost identical except that one of them is async and returns a promise that resolves promises returned by the callback method and the other one isn't. The 2 functions are really identical, but because the callback is called in a loop, I don't really have any good idea on how to extract the common part while still waiting for the resolution of all promises. Any suggestions?

  iterate(callback) {
    let firstIteration = true;
    let lastIp;
    while (lastIp = (firstIteration && this.startingIp ? this.startingIp : this.subnet.nextIp(lastIp))) {
      firstIteration = false;
      callback(lastIp, this.subnet.buildProgressData(lastIp));
    }
  }

  async iterateAsync(callback) {
    let firstIteration = true;
    let lastIp;
    while (lastIp = (firstIteration && this.startingIp ? this.startingIp : this.subnet.nextIp(lastIp))) {
      firstIteration = false;
      await callback(lastIp, this.subnet.buildProgressData(lastIp));
    }
  }
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Kamil Janowski
  • 1,872
  • 2
  • 21
  • 43
  • 1
    You can use `await` for values that are not promises: `let result = await 22` So there is no use for your sync iterate, just use iterateAsync for both. – HMR Apr 11 '18 at 08:14
  • yes, you can, but in order to be able to use 'await' I have to first use 'async' on the method. Then the method will always return a promise and this is not what you want if the callback isn't asynchronous – Kamil Janowski Apr 11 '18 at 08:47
  • In your example you are not returning anything so why should that be a problem? – HMR Apr 11 '18 at 11:11
  • the second method is async and therefore it returns a promise. The first one is not really supposed to return anything. Now I'd like to use the same code to either return a promise or return nothing – Kamil Janowski Apr 11 '18 at 12:25

1 Answers1

2

Just write an iterator for your instance instead of an iterate method with a callback, so the caller can decide himself how to consume it.

[Symbol.iterator]*() {
  let firstIteration = true;
  let lastIp;
  while (lastIp = (firstIteration && this.startingIp ? this.startingIp : this.subnet.nextIp(lastIp))) {
    firstIteration = false;
    yield [lastIp, this.subnet.buildProgressData(lastIp)];
  }
}
iterate(callback) {
  for (const [ip, progress] of this)
    callback(ip, progress);
}
async iterateAsync(callback) {
  for (const [ip, progress] of this)
    await callback(ip, progress);
}

You probably don't need those iterate methods at all, using for … of is much simpler.

With generator functions, you can also easily simplify your logic to

[Symbol.iterator]*() {
  let ip;
  if (ip = this.startingIp)
    yield this.startingIp;

  while (ip = this.subnet.nextIp(ip))
    yield [ip, this.subnet.buildProgressData(ip)];
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I didn't know about the iterators in JS. Nice. Looks legit. Thanks – Kamil Janowski Apr 11 '18 at 12:30
  • Notice that `[Symbol.iterator]` is just the default method used when you try to iterate the object, as in `for (… of this)`. You can also use your own method name if that's more suitable, such as `getAllIps*() {…` and `for (… of this.getAllIps())` – Bergi Apr 11 '18 at 12:32