OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // TODO(lrn): This should be in package:async? |
| 6 /** |
| 7 * Helper functions for working with errors. |
| 8 * |
| 9 * The [MultiError] class combines multiple errors into one object, |
| 10 * and the [MultiError.wait] function works like [Future.wait] except |
| 11 * that it returns all the errors. |
| 12 */ |
| 13 library pkg.isolate.errors; |
| 14 |
| 15 import "dart:async"; |
| 16 |
| 17 class MultiError extends Error { |
| 18 // Limits the number of lines included from each error's error message. |
| 19 // A best-effort attempt is made at keeping below this number of lines |
| 20 // in the output. |
| 21 // If there are too many errors, they will all get at least one line. |
| 22 static const int _MAX_LINES = 55; |
| 23 // Minimum number of lines in the toString for each error. |
| 24 static const int _MIN_LINES_PER_ERROR = 1; |
| 25 |
| 26 /** The actual errors. */ |
| 27 final List errors; |
| 28 |
| 29 /** |
| 30 * Create a `MultiError` based on a list of errors. |
| 31 * |
| 32 * The errors represent errors of a number of individual operations. |
| 33 * |
| 34 * The list may contain `null` values, if the index of the error in the |
| 35 * list is useful. |
| 36 */ |
| 37 MultiError(this.errors); |
| 38 |
| 39 /** |
| 40 * Waits for all [futures] to complete, like [Future.wait]. |
| 41 * |
| 42 * Where `Future.wait` only reports one error, even if multiple |
| 43 * futures complete with errors, this function will complete |
| 44 * with a [MultiError] if more than one future completes with an error. |
| 45 * |
| 46 * The order of values is not preserved (if that is needed, use |
| 47 * [wait]). |
| 48 */ |
| 49 static Future<List> waitUnordered(Iterable<Future> futures, |
| 50 {cleanUp(successResult)}) { |
| 51 Completer completer; |
| 52 int count = 0; |
| 53 int errors = 0; |
| 54 int values = 0; |
| 55 // Initilized to `new List(count)` when count is known. |
| 56 // Filled up with values on the left, errors on the right. |
| 57 // Order is not preserved. |
| 58 List results; |
| 59 void checkDone() { |
| 60 if (errors + values < count) return; |
| 61 if (errors == 0) { |
| 62 completer.complete(results); |
| 63 return; |
| 64 } |
| 65 var errorList = results.sublist(results.length - errors); |
| 66 completer.completeError(new MultiError(errorList)); |
| 67 }; |
| 68 var handleValue = (v) { |
| 69 // If this fails because [results] is null, there is a future |
| 70 // which breaks the Future API by completing immediately when |
| 71 // calling Future.then, probably by misusing a synchronous completer. |
| 72 results[values++] = v; |
| 73 if (errors > 0 && cleanUp != null) { |
| 74 new Future.sync(() => cleanUp(v)); |
| 75 } |
| 76 checkDone(); |
| 77 }; |
| 78 var handleError = (e, s) { |
| 79 if (errors == 0 && cleanUp != null) { |
| 80 for (int i = 0; i < values; i++) { |
| 81 var value = results[i]; |
| 82 if (value != null) new Future.sync(() => cleanUp(value)); |
| 83 } |
| 84 } |
| 85 results[results.length - ++errors] = e; |
| 86 checkDone(); |
| 87 }; |
| 88 for (Future future in futures) { |
| 89 count++; |
| 90 future.then(handleValue, onError: handleError); |
| 91 } |
| 92 if (count == 0) return new Future.value(new List(0)); |
| 93 results = new List(count); |
| 94 completer = new Completer(); |
| 95 return completer.future; |
| 96 } |
| 97 |
| 98 /** |
| 99 * Waits for all [futures] to complete, like [Future.wait]. |
| 100 * |
| 101 * Where `Future.wait` only reports one error, even if multiple |
| 102 * futures complete with errors, this function will complete |
| 103 * with a [MultiError] if more than one future completes with an error. |
| 104 * |
| 105 * The order of values is preserved, and if any error occurs, the |
| 106 * [MultiError.errors] list will have errors in the corresponding slots, |
| 107 * and `null` for non-errors. |
| 108 */ |
| 109 Future<List> wait(Iterable<Future> futures, |
| 110 {cleanUp(successResult)}) { |
| 111 Completer completer; |
| 112 int count = 0; |
| 113 bool hasError = false; |
| 114 int completed = 0; |
| 115 // Initalized to `new List(count)` when count is known. |
| 116 // Filled with values until the first error, then cleared |
| 117 // and filled with errors. |
| 118 List results; |
| 119 void checkDone() { |
| 120 completed++; |
| 121 if (completed < count) return; |
| 122 if (!hasError) { |
| 123 completer.complete(results); |
| 124 return; |
| 125 } |
| 126 completer.completeError(new MultiError(results)); |
| 127 }; |
| 128 for (Future future in futures) { |
| 129 int i = count; |
| 130 count++; |
| 131 future.then((v) { |
| 132 if (!hasError) { |
| 133 results[i] = v; |
| 134 } else if (cleanUp != null) { |
| 135 new Future.sync(() => cleanUp(v)); |
| 136 } |
| 137 checkDone(); |
| 138 }, onError: (e, s) { |
| 139 if (!hasError) { |
| 140 if (cleanUp != null) { |
| 141 for (int i = 0; i < results.length; i++) { |
| 142 var result = results[i]; |
| 143 if (result != null) new Future.sync(() => cleanUp(result)); |
| 144 } |
| 145 } |
| 146 results.fillRange(0, results.length, null); |
| 147 hasError = true; |
| 148 } |
| 149 results[i] = e; |
| 150 checkDone(); |
| 151 }); |
| 152 } |
| 153 if (count == 0) return new Future.value(new List(0)); |
| 154 results = new List(count); |
| 155 completer = new Completer(); |
| 156 return completer.future; |
| 157 } |
| 158 |
| 159 |
| 160 String toString() { |
| 161 StringBuffer buffer = new StringBuffer(); |
| 162 buffer.write("Multiple Errors:\n"); |
| 163 int linesPerError = _MAX_LINES ~/ errors.length; |
| 164 if (linesPerError < _MIN_LINES_PER_ERROR) { |
| 165 linesPerError = _MIN_LINES_PER_ERROR; |
| 166 } |
| 167 |
| 168 for (int index = 0; index < errors.length; index++) { |
| 169 var error = errors[index]; |
| 170 if (error == null) continue; |
| 171 String errorString = error.toString(); |
| 172 int end = 0; |
| 173 for (int i = 0; i < linesPerError; i++) { |
| 174 end = errorString.indexOf('\n', end) + 1; |
| 175 if (end == 0) { |
| 176 end = errorString.length; |
| 177 break; |
| 178 } |
| 179 } |
| 180 buffer.write("#$index: "); |
| 181 buffer.write(errorString.substring(0, end)); |
| 182 if (end < errorString.length) { |
| 183 buffer.write("...\n"); |
| 184 } |
| 185 } |
| 186 return buffer.toString(); |
| 187 } |
| 188 } |
OLD | NEW |