-
-
Save 1gor/aeeb8d26e8edc9e95a45e0d197dfb971 to your computer and use it in GitHub Desktop.
Rendering a promise with Clearwater
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'clearwater/black_box_node' | |
| require 'clearwater/component' | |
| require 'clearwater/application' | |
| class RenderPromise | |
| include Clearwater::BlackBoxNode | |
| DEFAULT = proc { Clearwater::Component.span } | |
| Wrapper = Struct.new(:render).include(Clearwater::Component) | |
| attr_reader :app, :started_at, :wrapper, :promise | |
| def initialize promise, &loaded | |
| @promise = promise | |
| @delay = {} | |
| @loaded = loaded || DEFAULT | |
| @begin = DEFAULT | |
| @error = DEFAULT | |
| @timer_ids = [] | |
| end | |
| def node | |
| @begin.call | |
| end | |
| def begin &block | |
| @begin = block if block_given? | |
| self | |
| end | |
| def delay seconds, &block | |
| @delay[seconds] = block if block_given? | |
| self | |
| end | |
| def loaded &block | |
| @loaded = block if block_given? | |
| self | |
| end | |
| def error &block | |
| @error = block if block_given? | |
| self | |
| end | |
| def mount element | |
| @started_at = Time.now | |
| @active = true | |
| @wrapper ||= Wrapper.new | |
| @app ||= Clearwater::Application.new( | |
| component: @wrapper, | |
| element: element, | |
| ) | |
| wait_for_resolution | |
| end | |
| def update previous, element | |
| @app = previous.app | |
| @wrapper = previous.wrapper | |
| previous.deactivate! | |
| if @promise.equal? previous.promise | |
| @started_at = previous.started_at | |
| else | |
| mount element | |
| return | |
| end | |
| if @promise.resolved? | |
| @wrapper.render = @loaded.call @promise.value | |
| @app.render | |
| elsif @promise.rejected? | |
| @wrapper.render = @error.call @promise.error | |
| @app.render | |
| else | |
| wait_for_resolution | |
| end | |
| rescue => e | |
| @wrapper.render = @error.call e | |
| @app.render | |
| end | |
| def unmount element | |
| deactivate! | |
| end | |
| def wait_for_resolution | |
| age = Time.now - started_at | |
| render = @delay | |
| .sort_by { |seconds, _| seconds } | |
| .take_while { |seconds, _| age >= seconds.first } | |
| wrapper.render = (render.last || [0, @begin]).last.call | |
| # Stupid solution for exponential renders, but it works. | |
| @current_render = rand | |
| current_render = @current_render | |
| @delay | |
| .map { |seconds, _| seconds - age } | |
| .select { |seconds| seconds > 0 } | |
| .each do |seconds| | |
| set_timer seconds do | |
| if active? && current_render == @current_render | |
| wait_for_resolution | |
| end | |
| end | |
| end | |
| @promise.then do |value| | |
| if active? | |
| @wrapper.render = begin | |
| @loaded.call value | |
| rescue => e | |
| @error.call e | |
| end | |
| @app.render | |
| end | |
| end | |
| @promise.catch do |error| | |
| if active? | |
| warn error | |
| @wrapper.render = @error.call error | |
| @app.render | |
| end | |
| end | |
| @app.render | |
| end | |
| def active? | |
| @active | |
| end | |
| def deactivate! | |
| @active = false | |
| @timer_ids.each do |id| | |
| `clearTimeout(id)` | |
| end | |
| end | |
| def set_timer delay, &block | |
| @timer_ids << `setTimeout(function() { #{block.call} }, delay * 1000)` | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment