AllOf рдФрд░ AnyOf рдХреЛ рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА рдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд░рдирд╛



рдлрд┐рд░ рд╕реЗ рдирдорд╕реНрдХрд╛рд░ред рдкрд╛рдареНрдпрдХреНрд░рдо рдХреА рд╢реБрд░реБрдЖрдд рдХреА рдкреНрд░рддреНрдпрд╛рд╢рд╛ рдореЗрдВ "рдЬрд╛рд╡рд╛ рдбреЗрд╡рд▓рдкрд░" рдиреЗ рдЖрдкрдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рд╕рд╛рдордЧреНрд░реА рдХрд╛ рдЕрдиреБрд╡рд╛рдж рддреИрдпрд╛рд░ рдХрд┐рдпрд╛ред


CompletableFuture рдореЗрдВ рджреЛ рд╡рд┐рдзрд┐рдпрд╛рдБ рд╣реИрдВ рдЬрд┐рдирдХреА рдбрд┐рдЬрд╝рд╛рдЗрди рдореБрдЭреЗ рдЖрд╢реНрдЪрд░реНрдпрдЪрдХрд┐рдд рдХрд░рддреА рд╣реИ:

  • рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕реАрд╡рди # allOf
  • рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА # anyOf


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рд╣рдо рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдЙрдирдХреЗ рд╕рд╛рде рдХреНрдпрд╛ рдЧрд▓рдд рд╣реИ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдХреИрд╕реЗ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕реАрд╡рди # allOf


рдЖрдЗрдП рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдкрд░ рдирдЬрд░ рдбрд╛рд▓реЗрдВ:

 public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) { // ... } 


рдпрд╣рд╛рдВ рдХрдо рд╕реЗ рдХрдо рджреЛ рд╡рд┐рд╡рд╛рджрд╛рд╕реНрдкрдж рдореБрджреНрджреЗ рд╣реИрдВ:

  1. рдпрд╣ рд╡рд┐рдзрд┐ рдХрдИ CompletableFuture рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддреА рд╣реИ рдЬреЛ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░ рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреЛ CompletableFuture ред
  2. рдореЗрдердб CompletableFuture , рдЬреЛ Void рджреЗрддрд╛ рд╣реИ


рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдХреБрдЫ рдкреИрд░рд╛рдореАрдЯрд░ рдХреА рдЪрд░ рд╕рдВрдЦреНрдпрд╛ рдкрд╕рдВрдж рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рддреЛ рдЖрдЗрдП рдЗрд╕ рднрд╛рдЧ рдХреЛ рднреА рджреЗрдЦреЗрдВред

CompletableFuture<Void> рдЕрдХреНрд╕рд░ рдХрд┐рд╕реА рдСрдкрд░реЗрд╢рди рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд┐рдЧреНрдирд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ; рд╣рд╛рд▓рд╛рдБрдХрд┐, рдПрдкреАрдЖрдИ рдореЗрдВ рдПрдХ рдЫреЛрдЯрд╛ рд╕рд╛ рдкрд░рд┐рд╡рд░реНрддрди рдХрд░рдХреЗ, рдЗрд╕ рд╡рд┐рдзрд┐ рдХреЛ рд╕рд┐рдЧреНрдирд▓рд┐рдВрдЧ рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рдФрд░ рд╕рднреА рдкреВрд░реНрдг рдХрд┐рдП рдЧрдП рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рд╡рд╛рд╣рдХ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред рдЪрд▓реЛ рдЗрд╕реЗ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВред

рдПрд╕рд┐рдВрдХреНрд░реЛрдирд╕ рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА # рдСрд▓рдСрдл


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЪрд▓реЛ рд╕рд╣реА рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЗ рд╕рд╛рде рдЖрддреЗ рд╣реИрдВред

рдпрд╣ рдорд╛рди рд▓реЗрдирд╛ рдЙрдЪрд┐рдд рд╣реИ рдХрд┐ рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рд╕рдЬрд╛рддреАрдп CompletableFuture рдХреА рдПрдХ рд╕реВрдЪреА рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдирд╛ рдФрд░ рдПрдХ CompletableFuture рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдирд╛ рдЬрд┐рд╕рдореЗрдВ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреА рд╕реВрдЪреА рд╣реЛрдЧреА, рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА:

 public static <T> CompletableFuture<List<T>> allOf( Collection<CompletableFuture<T>> futures) { // ... } 


рдореВрд▓ рд╡рд┐рдзрд┐ рдХрд╛ рдЕрдВрддрд░ рдЖрдкрдХреА рдЕрдкреЗрдХреНрд╖рд╛ рд╕реЗ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реИ:

 static CompletableFuture<Void> andTree( CompletableFuture<?>[] cfs, int lo, int hi) { CompletableFuture<Void> d = new CompletableFuture<Void>(); if (lo > hi) // empty d.result = NIL; else { CompletableFuture<?> a, b; int mid = (lo + hi) >>> 1; if ((a = (lo == mid ? cfs[lo] : andTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : andTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); if (!d.biRelay(a, b)) { BiRelay<?,?> c = new BiRelay<>(d, a, b); a.bipush(b, c); c.tryFire(SYNC); } } return d; } 


рдЗрд╕рд▓рд┐рдП, рдЗрд╕реЗ рд╕реНрдХреНрд░реИрдЪ рд╕реЗ рдмрдирд╛рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рд╣рдо рдкреБрди: рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдореВрд▓ рд╡рд┐рдзрд┐ рдореЗрдВ рд╣реИ рдЬреИрд╕реЗ рдХрд┐ рдЗрд╕реЗ рдПрдХ рдкреВрд░реНрдг рд╕рд┐рдЧреНрдирд▓рд┐рдВрдЧ рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдЗрд░рд╛рджрд╛ рдерд╛ ... рдФрд░ рдлрд┐рд░ рд╢реВрдиреНрдп рдкрд░рд┐рдгрд╛рдо рдХреЛ рднрд╡рд┐рд╖реНрдп рдХреА рд╕реВрдЪреА рдореЗрдВ рдмрджрд▓ рджреЗрдВ:

 CompletableFuture<List<CompletableFuture<T>>> i = futures.stream() .collect(collectingAndThen( toList(), l -> CompletableFuture.allOf(l.toArray(new CompletableFuture[0])) .thenApply(__ -> l))); 


рдЕрднреА рддрдХ рддреЛ рдЕрдЪреНрдЫрд╛ рд╣реИред рд╣рдо рдкрд╛рдиреЗ рдореЗрдВ рдХрд╛рдордпрд╛рдм рд░рд╣реЗ
CompletableFuture<List<CompletableFuture<T> > > рдмрдЬрд╛рдп CompletableFuture<List<CompletableFuture<T> > > CompletableFuture<Void> рдЬреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЕрдЪреНрдЫрд╛ рд╣реИред рд▓реЗрдХрд┐рди рд╣рдореЗрдВ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рд╕рд╛рде рднрд╡рд┐рд╖реНрдп рдХреА рд╕реВрдЪреА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рд╣рдореЗрдВ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреА рд╕реВрдЪреА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдЕрдм рд╣рдо рдХреЗрд╡рд▓ рд╕реВрдЪреА рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕рдореЗрдВ рд╕реЗ рдЕрд╡рд╛рдВрдЫрд┐рдд рднрд╡рд┐рд╖реНрдп рдирд┐рдХрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВред рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА # рдЬреЙрдЗрди рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рд╛рдорд╛рдиреНрдп рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╡реЗ рдХрднреА рднреА рдЕрд╡рд░реБрджреНрдз рдирд╣реАрдВ рд╣реЛрдВрдЧреЗ (рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░, рд╕рднреА рднрд╡рд┐рд╖реНрдп рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдкреВрд░реЗ рд╣реЛ рдЪреБрдХреЗ рд╣реИрдВ):

 CompletableFuture<List<T>> result = intermediate .thenApply(list -> list.stream() .map(CompletableFuture::join) .collect(toList())); 


рдЕрдм рдЗрд╕ рд╕рдм рдХреЛ рдПрдХ рдЕрдВрддрд┐рдо рд╣рд▓ рдореЗрдВ рдорд┐рд▓рд╛рддреЗ рд╣реИрдВ:

 public static <T> CompletableFuture<List<T>> allOf( Collection<CompletableFuture<T>> futures) { return futures.stream() .collect(collectingAndThen( toList(), l -> CompletableFuture.allOf(l.toArray(new CompletableFuture[0])) .thenApply(__ -> l.stream() .map(CompletableFuture::join) .collect(Collectors.toList())))); } 


рдПрд╕рд┐рдВрдХреНрд░реЛрдирд╕ рдФрд░ рдлреЙрд▓рд┐рдВрдЧ рдХрдореНрдкреЗрдЯрд┐рдмрд▓рдлреНрдпреВрдЪрд░ # allOf


рдпрджрд┐ рдЕрдкрд╡рд╛рдж рд╣реИрдВ, рддреЛ рдореВрд▓ рдХрдВрдкреЛрдЬрд╝рд┐рдмрд▓рдлрд╝реНрдпреВрдЬрд╝рди # allOf рд╕рднреА рд╢реЗрд╖ рд╕рдВрдЪрд╛рд▓рди рдХреЗ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддрд╛ рд╣реИред

рдФрд░ рдЕрдЧрд░ рд╣рдо рдЗрд╕рдореЗрдВ рдЕрдкрд╡рд╛рдж рд╣реЛрдиреЗ рдкрд░ рдСрдкрд░реЗрд╢рди рдХреЗ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреА рд░рд┐рдкреЛрд░реНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рд╣рдореЗрдВ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдмрджрд▓рдирд╛ рд╣реЛрдЧрд╛ред

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, CompletableFuture рдХрд╛ рдПрдХ рдирдпрд╛ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛рдПрдВ рдФрд░ рдСрдкрд░реЗрд╢рди рдХреЗ рдПрдХ рдЕрдкрд╡рд╛рдж рдХреЗ рдмрд╛рдж рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдЗрд╕реЗ рд╕рдорд╛рдкреНрдд рдХрд░реЗрдВ:

 CompletableFuture<List<T>> result = new CompletableFuture<>(); futures.forEach(f -> f .handle((__, ex) -> ex == null || result.completeExceptionally(ex))); 


... рд▓реЗрдХрд┐рди рддрдм рд╣рдореЗрдВ рдкрд░рд┐рджреГрд╢реНрдп рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ рдЬрдм рд╕рднреА рднрд╡рд┐рд╖реНрдп рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдкреВрд░рд╛ рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред рдпрд╣ рдЖрд╕рд╛рдиреА рд╕реЗ рдмреЗрд╣рддрд░ рдСрд▓рдл () рдкрджреНрдзрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдлрд┐рд░ рдмрд╕ рднрд╡рд┐рд╖реНрдп рдХреЛ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рд╕рдорд╛рдкреНрдд рдХрд░ рд╕рдХрддрд╛ рд╣реИ:

 allOf(futures).thenAccept(result::complete); 


рдЕрдм рд╣рдо рдЕрдВрддрд┐рдо рд╕рдорд╛рдзрд╛рди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдм рдХреБрдЫ рдПрдХ рд╕рд╛рде рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ:

 public static <T> CompletableFuture<List<T>> allOfShortcircuiting(Collection<CompletableFuture<T>> futures) { CompletableFuture<List<T>> result = new CompletableFuture<>(); for (CompletableFuture<?> f : futures) { f.handle((__, ex) -> ex == null || result.completeExceptionally(ex)); } allOf(futures).thenAccept(result::complete); return result; } 


рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА # anyOf


рдЖрдЗрдП рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ:

 public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) { // ... } 


рд╣рдо рддреБрд░рдВрдд рдЙрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдКрдкрд░ рдЪрд░реНрдЪрд╛ рдХреА рдЧрдИ рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рд╣реИрдВ:

  1. рд╡рд┐рдзрд┐ рдХрдИ рдкреНрд░рдХрд╛рд░ рдХреЗ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рд╡рд╛рд▓реЗ рдХрдИ CompletableFuture рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддреА рд╣реИред
  2. рд╡рд┐рдзрд┐ рдПрдХ рдкреВрд░реНрдг рдкреНрд░рдХрд╛рд░ рдХрд╛ Object рдпреБрдХреНрдд рдПрдХ CompletableFuture рджреЗрддрд╛ рд╣реИред


рдЬрд╣рд╛рдВ рддрдХ тАЛтАЛрдореИрдВ рд╕рдордЭрддрд╛ рд╣реВрдВ, CompletableFuture#allOf рдХреЛ рдПрдХ рд╕рд┐рдЧреНрдирд▓рд┐рдВрдЧ рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рд▓реЗрдХрд┐рди CompletableFuture#anyOf рдЗрд╕ CompletableFuture#anyOf рдХреЛ рдлреЙрд▓реЛ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, CompletableFuture#anyOf CompletableFuture<Object> рд▓реМрдЯрд╛рддрд╛ рд╣реИ, рдЬреЛ рдХрд┐ рдФрд░ рднреА рднреНрд░рд╛рдордХ рд╣реИред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЙрджрд╛рд╣рд░рдг рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВ рдЬрд╣рд╛рдБ рдореИрдВ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рдбреЗрдЯрд╛ рдпреБрдХреНрдд рдХрдВрдкреНрд▓реАрдЯрдЯреЗрдмрд▓ рд╕рд┐рд╡рдиреА рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд░рд╣рд╛ рд╣реВрдБ:

 CompletableFuture<Integer> f1 = CompletableFuture.completedFuture(1); CompletableFuture<String> f2 = CompletableFuture.completedFuture("2"); Integer result = CompletableFuture.anyOf(f1, f2) .thenApply(r -> { if (r instanceof Integer) { return (Integer) r; } else if (r instanceof String) { return Integer.valueOf((String) r); } throw new IllegalStateException("unexpected object type!"); }).join(); 


рдмрд╣реБрдд рдЕрд╕рд╣рдЬ рд╣реИ, рд╣реИ рдирд╛?

рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдмрджрд▓рдиреЗ рдФрд░ рдкреНрд░рддреНрдпрдХреНрд╖ рдкреНрд░рдХрд╛рд░ рдХреА рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рд╢реБрд░реВ рдХрд░рдиреЗ рд╕реЗ рдЕрдзрд┐рдХ рд╕рд╛рдорд╛рдиреНрдп рдкрд░рд┐рджреГрд╢реНрдп (рдПрдХ рд╣реА рдкреНрд░рдХрд╛рд░ рдХреЗ рдХрдИ рднрд╡рд┐рд╖реНрдп рдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдирд╛) рдХреЗ рд▓рд┐рдП рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░рдирд╛ рдмрд╣реБрдд рдЖрд╕рд╛рди рд╣реИред

рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╣рдорд╛рд░реЗ рд╕реБрдзрд╛рд░реЛрдВ рдХреЗ рд╕рд╛рде, рд╣рдо рдореМрдЬреВрджрд╛ рддрд░реАрдХреЛрдВ рдХрд╛ рдкреБрди: рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рд╕реБрд░рдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рдкрд░рд┐рдгрд╛рдо рд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВ:

 public static <T> CompletableFuture<T> anyOf(List<CompletableFuture<T>> cfs) { return CompletableFuture.anyOf(cfs.toArray(new CompletableFuture[0])) .thenApply(o -> (T) o); } public static <T> CompletableFuture<T> anyOf(CompletableFuture<T>... cfs) { return CompletableFuture.anyOf(cfs).thenApply(o -> (T) o); } 


рд╕реНрд░реЛрдд рдХреЛрдб


рд╕реНрд░реЛрдд рдХреЛрдб Github рдкрд░ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рд╡рд╣ рд╕рдм рд╣реИред рдХреЛрд░реНрд╕ рдкрд░ рдорд┐рд▓рддреЗ рд╣реИрдВ ред

Source: https://habr.com/ru/post/hi481804/


All Articles