Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.map and Promise.filter #37

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

arthurschreiber
Copy link
Contributor

@arthurschreiber arthurschreiber commented Oct 23, 2017

This adds 2 new methods, Promise.map and Promise.filter, heavily inspired by the equally named methods from the great bluebird.js Promise library for JavaScript.

Promise.map

Promise.map is similar to Promise.all, except that the values resolved from the input enumerable will be mapped using the provided block. This block can either return Promise or non-Promise objects. The promise returned by Promise.map will be resolved once all values have been mapped.

Example

values = [1, 2, Promise.resolve(3), 4, 5, Promise.resolve(6)]

async_mapped_values = Promise.map(values) { |value|
  Promise.resolve(value + 1)
}

async_mapped_values.then do |result|
  p result # => [2, 3, 4, 5, 6, 7]
end

Without Promise.map, the same thing can be achieved like this:

values = [1, 2, Promise.resolve(3), 4, 5, Promise.resolve(6)]

Promise.all(values).then do |resolved_values|
  async_mapped_values = Promise.all(resolved_values.map { |value| Promise.resolve(value + 1) })

  async_mapped_values.then do |result|
    p result # => [2, 3, 4, 5, 6, 7]
  end
end

Advantages

Besides the improved ease-of-use, the native Promise.map has better performance over the solution build on Promise.all. Promise.map can interleave the process of resolving values and mapping them to their final value, while these are two separate steps in the alternative version using Promise.all.

Promise.filter

Promise.filter allows filtering an enumerable containing Promise and/or non-Promise values, based on the given filter block. The filter block can return either Promise or non-Promise values.

Example

values = [1, 2, Promise.resolve(3), 4, 5, Promise.resolve(6)]

async_filtered_values = Promise.filter(values) { |value|
  Promise.resolve(value.even?)
}

async_filtered_values.then do |result|
  p result # => [2, 4, 6]
end

Without Promise.filter, the same thing can be achieved like this:

values = [1, 2, Promise.resolve(3), 4, 5, Promise.resolve(6)]

Promise.all(values).then do |resolved_values|
  async_filter_results = Promise.all(resolved_values.map { |value| Promise.resolve(value.even?) })

  async_filter_results.then do |filter_results|
    result = resolved_values.filter.with_index { |value, index| filter_results[index] }
    p result # => [2, 4, 6]
  end
end

Advantages

Again, the version built on Promise.filter is a lot easier to understand. Similar to Promise.map, Promise.filter can perform the initial value resolution and filtering process at the same time, where as the version built on Promise.all performs two discrete steps.

@arthurschreiber
Copy link
Contributor Author

@dylanahsmith Heyo! 👋

Do you have any opinion regarding these extensions?

I had to pin the version of devtools, as it looks like a new version was released, coming with different checks that fail even as-is on the master branch. I can extract that change into a separate pull request.

There is also a few mutants not getting killed in the mutation tests, but I'm not sure how to properly fix those. Any idea?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant