I will start by saying that the original is a fundamentally bad API design. It 
exposes side effects of the method (namely modifying the passed in Set<Node>) 
which should not ever be part of a sensible API contract. This method could 
return a Set<Node> (or a Promise<Set<Node>>) indicating what it did, but 
actually that doesn’t seem to be needed - there’s really only one node to 
return each time. As a result it’s much cleaner and simpler to do the set 
addition and gathering outside this method.

  // Using a PromiseFactory is better as it gives more
  // control of threading. You’re already using 1.1
  PromiseFactory pf = new PromiseFactory();

  public Promise<Node> handleNodeAddition(Object element) {
    
    Promise<Node> p;

    Node node = diagram.getNode(element);
    if (node == null) {
      p = actionProvider.createNode(element, diagram);
      addedNodes.add(node);
    } else {
      p = pf.resolved(node);
    }

    // Using thenAccept means that you return a promise which resolves
    // *after* the synchronize. If you use onSuccess then the returned
    // promise will resolve *before* the synchronize and you may not
    // see the result of the synchronize in some of your other callbacks

    return p.thenAccept(actionProvider::synchronize);
  } 

The set gathering should then be done elsewhere, and without side-effects.

  
  public Promise<Set<Node>> doNodeAdditions(List<Object> elements) {    

    List<Promise<Node>> promises = new ArrayList<>(elements.size());

    Promise<Node> previous = pf.resolved(null);

    for(Object o : elements) {
        previous = previous.flatMap(x -> handleNodeAddition(o));
        promises.add(previous);
    }

    return pf.all(promises)
             .map(HashSet::new);

    // You could also use this as the promises are all in a chain
    //  return previous.map(x -> promises.stream()
    //                                   .map(Promise::getValue)
    //                                   .collect(Collectors.toSet()));
  } 

I hope that this helps

Best Regards,

Tim

> On 5 Oct 2018, at 00:28, Olivier Labrosse via osgi-dev 
> <[email protected]> wrote:
> 
> Hi,
> 
> I'm dealing with code refactoring from a synchronous system to an 
> asynchronous one, using OSGi Promises.  One pattern we have goes as follows:
> 
>   public void handleNodeAddition(Object element, Set<Node> addedNodes) {
>     Node node = diagram.getNode(element);
>     if (node == null) {
>       node = actionProvider.createNode(element, diagram);
>       addedNodes.add(node);
>     }
>     actionProvider.synchronize(node);
>   }
> 
> The problem I'm facing is that actionProvider.createNode() now returns a 
> Promise<Node> due to asynchronous execution.  This means we can no longer 
> just add nodes to the Set, but not only this, we have to make sure that each 
> createNode() call from this thread happens after the previous one is resolved.
> 
> Would there be a best practice for this kind of process?  If I were to keep 
> the pattern as-is but implement support for asynchronous node creation, 
> here's how I would do it:
> 
>   public void handleNodeAddition(Object element,
>       AtomicReference<Promise<Set<Node>>> addedNodes) {
>     Promises.resolved(diagram.getNode(element))
>         .then(existingNode -> {
>           Node node = existingNode.getValue();
>           Promise<Node> nodePromise;
>           
>           if (node == null) {
>             // Using an AtomicReference so the Promise chain can be updated
>             Promise<Set<Node>> addedNodesPromise = addedNodesPromiseRef.get();
>             
>             nodePromise = addedNodesPromise // wait for previous node to be 
> added
>                 .then(previousNodeAdded -> actionProvider.createNode(element, 
> diagram))
>                 .onSuccess(createdNode -> createdNode.setLocation(location));
>             
>             addedNodesPromiseRef.set(createNodePromise
>                 .then(createdNode -> {
>                   addedNodesPromise.getValue().add(createdNode.getValue());
>                   return addedNodesPromise; // still holds the Set
>                 })
>             );
>           }
>           else {
>             nodePromise = Promises.resolved(node);
>           }
>           
>           return nodePromise;
>         })
>         .onSuccess(nodeToSync -> actionProvider.synchronize(nodeToSync));
>   }
> 
> Any and all advice is much appreciated, thank you!
> 
> -Olivier Labrosse
> _______________________________________________
> OSGi Developer Mail List
> [email protected]
> https://mail.osgi.org/mailman/listinfo/osgi-dev

_______________________________________________
OSGi Developer Mail List
[email protected]
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to