зеркало из https://github.com/mozilla/gecko-dev.git
Bug 681843 - Part 1: Clean up async.js. r=rnewman
This commit is contained in:
Родитель
1476288c81
Коммит
beb1d69651
|
@ -129,157 +129,6 @@ let Async = {
|
|||
return callback;
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously invoke a method that takes only a `callback` argument.
|
||||
*/
|
||||
callSpinningly: function callSpinningly(self, method) {
|
||||
let callback = this.makeSpinningCallback();
|
||||
method.call(self, callback);
|
||||
return callback.wait();
|
||||
},
|
||||
|
||||
/*
|
||||
* Produce a sequence of callbacks which -- when all have been executed
|
||||
* successfully *or* any have failed -- invoke the output callback.
|
||||
*
|
||||
* Returns a generator.
|
||||
*
|
||||
* Each input callback should have the signature (error, result), and should
|
||||
* return a truthy value if the computation should be considered to have
|
||||
* failed.
|
||||
*
|
||||
* The contents of ".data" on each input callback are copied to the
|
||||
* resultant callback items. This can save some effort on the caller's side.
|
||||
*
|
||||
* These callbacks are assumed to be single- or double-valued (a "result" and
|
||||
* a "context", say), which covers the common cases without the expense of
|
||||
* `arguments`.
|
||||
*/
|
||||
barrieredCallbacks: function (callbacks, output) {
|
||||
if (!output) {
|
||||
throw "No output callback provided to barrieredCallbacks.";
|
||||
}
|
||||
|
||||
let counter = callbacks.length;
|
||||
function makeCb(input) {
|
||||
let cb = function(error, result, context) {
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
let err;
|
||||
try {
|
||||
err = input(error, result, context);
|
||||
} catch (ex) {
|
||||
output(ex);
|
||||
output = undefined;
|
||||
return;
|
||||
}
|
||||
if ((0 == --counter) || err) {
|
||||
output(err);
|
||||
output = undefined;
|
||||
}
|
||||
};
|
||||
cb.data = input.data;
|
||||
return cb;
|
||||
}
|
||||
return (makeCb(i) for each (i in callbacks));
|
||||
},
|
||||
|
||||
/*
|
||||
* Similar to barrieredCallbacks, but with the same callback each time.
|
||||
*/
|
||||
countedCallback: function (componentCb, count, output) {
|
||||
if (!output) {
|
||||
throw "No output callback provided to countedCallback.";
|
||||
}
|
||||
|
||||
if (!count || (count <= 0)) {
|
||||
throw "Invalid count provided to countedCallback.";
|
||||
}
|
||||
|
||||
let counter = count;
|
||||
return function (error, result, context) {
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
let err;
|
||||
try {
|
||||
err = componentCb(error, result, context);
|
||||
} catch (ex) {
|
||||
output(ex);
|
||||
// We're done; make sure output callback is only called once.
|
||||
output = undefined;
|
||||
return;
|
||||
}
|
||||
if ((0 == --counter) || err) {
|
||||
output(err); // If this throws, then... oh well.
|
||||
output = undefined;
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/*
|
||||
* Invoke `f` with each item and a wrapped version of `componentCb`.
|
||||
* When each component callback is invoked, the next invocation of `f` is
|
||||
* begun, unless the return value is truthy. (See barrieredCallbacks.)
|
||||
*
|
||||
* Finally, invoke the output callback.
|
||||
*
|
||||
* If there are no items, the output callback is invoked immediately.
|
||||
*/
|
||||
serially: function serially(items, f, componentCb, output) {
|
||||
if (!output) {
|
||||
throw "No output callback provided to serially.";
|
||||
}
|
||||
|
||||
if (!items || !items.length) {
|
||||
output();
|
||||
return;
|
||||
}
|
||||
|
||||
let count = items.length;
|
||||
let i = 0;
|
||||
function cb(error, result, context) {
|
||||
let err = error;
|
||||
if (!err) {
|
||||
try {
|
||||
err = componentCb(error, result, context);
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
}
|
||||
if ((++i == count) || err) {
|
||||
output(err);
|
||||
return;
|
||||
}
|
||||
Utils.nextTick(function () { f(items[i], cb); });
|
||||
}
|
||||
f(items[i], cb);
|
||||
},
|
||||
|
||||
/*
|
||||
* Return a callback which executes `f` then `callback`, regardless of
|
||||
* whether it was invoked with an error. If an exception is thrown during the
|
||||
* evaluation of `f`, it takes precedence over an error provided to the
|
||||
* callback.
|
||||
*
|
||||
* When used to wrap a callback, this offers similar behavior to try..finally
|
||||
* in plain JavaScript.
|
||||
*/
|
||||
finallyCallback: function (callback, f) {
|
||||
return function(err) {
|
||||
try {
|
||||
f();
|
||||
callback(err);
|
||||
} catch (ex) {
|
||||
callback(ex);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Prototype for mozIStorageCallback, used in querySpinningly.
|
||||
// This allows us to define the handle* functions just once rather
|
||||
// than on every querySpinningly invocation.
|
||||
|
|
|
@ -202,25 +202,6 @@ let Utils = {
|
|||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Partition the input array into an array of arrays. Return a generator.
|
||||
*/
|
||||
slices: function slices(arr, sliceSize) {
|
||||
if (!sliceSize || sliceSize <= 0)
|
||||
throw "Invalid slice size.";
|
||||
|
||||
if (sliceSize > arr.length) {
|
||||
yield arr;
|
||||
} else {
|
||||
let offset = 0;
|
||||
while (arr.length > offset) {
|
||||
yield arr.slice(offset, offset + sliceSize);
|
||||
offset += sliceSize;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
byteArrayToString: function byteArrayToString(bytes) {
|
||||
return [String.fromCharCode(byte) for each (byte in bytes)].join("");
|
||||
},
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
Cu.import("resource://services-sync/async.js");
|
||||
|
||||
function chain(fs) {
|
||||
fs.reduce(function (prev, next) next.bind(this, prev),
|
||||
run_next_test)();
|
||||
}
|
||||
|
||||
// barrieredCallbacks.
|
||||
add_test(function test_barrieredCallbacks() {
|
||||
let s1called = false;
|
||||
let s2called = false;
|
||||
|
||||
function reset() {
|
||||
_(" > reset.");
|
||||
s1called = s2called = false;
|
||||
}
|
||||
function succeed1(err, result) {
|
||||
_(" > succeed1.");
|
||||
s1called = true;
|
||||
}
|
||||
function succeed2(err, result) {
|
||||
_(" > succeed2.");
|
||||
s2called = true;
|
||||
}
|
||||
function fail1(err, result) {
|
||||
_(" > fail1.");
|
||||
return "failed";
|
||||
}
|
||||
function throw1(err, result) {
|
||||
_(" > throw1.");
|
||||
throw "Aieeee!";
|
||||
}
|
||||
|
||||
function doneSequential(next, err) {
|
||||
_(" > doneSequential.");
|
||||
do_check_eq(err, "failed");
|
||||
do_check_true(s1called);
|
||||
do_check_true(s2called);
|
||||
next();
|
||||
}
|
||||
function doneFailFirst(next, err) {
|
||||
_(" > doneFailFirst.");
|
||||
do_check_eq(err, "failed");
|
||||
do_check_false(s1called);
|
||||
do_check_false(s2called);
|
||||
next();
|
||||
}
|
||||
function doneOnlySucceed(next, err) {
|
||||
_(" > doneOnlySucceed.");
|
||||
do_check_true(!err);
|
||||
do_check_true(s1called);
|
||||
do_check_true(s2called);
|
||||
next();
|
||||
}
|
||||
function doneThrow(next, err) {
|
||||
_(" > doneThrow.");
|
||||
do_check_eq(err, "Aieeee!");
|
||||
do_check_true(s1called);
|
||||
do_check_false(s2called);
|
||||
next();
|
||||
}
|
||||
|
||||
function sequence_test(label, parts, end) {
|
||||
return function (next) {
|
||||
_("Sequence test '" + label + "':");
|
||||
reset();
|
||||
for (let cb in Async.barrieredCallbacks(parts, end.bind(this, next)))
|
||||
cb();
|
||||
};
|
||||
}
|
||||
|
||||
chain(
|
||||
[sequence_test("failFirst",
|
||||
[fail1, succeed1, succeed2],
|
||||
doneFailFirst),
|
||||
|
||||
sequence_test("sequentially",
|
||||
[succeed1, succeed2, fail1],
|
||||
doneSequential),
|
||||
|
||||
sequence_test("onlySucceed",
|
||||
[succeed1, succeed2],
|
||||
doneOnlySucceed),
|
||||
|
||||
sequence_test("throw",
|
||||
[succeed1, throw1, succeed2],
|
||||
doneThrow)]);
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_empty_barrieredCallbacks() {
|
||||
let err;
|
||||
try {
|
||||
Async.barrieredCallbacks([], function (err) { }).next();
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
_("err is " + err);
|
||||
do_check_true(err instanceof StopIteration);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_no_output_barrieredCallbacks() {
|
||||
let err;
|
||||
try {
|
||||
Async.barrieredCallbacks([function (x) {}], null);
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
do_check_eq(err, "No output callback provided to barrieredCallbacks.");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_serially() {
|
||||
let called = {};
|
||||
let i = 1;
|
||||
function reset() {
|
||||
called = {};
|
||||
i = 0;
|
||||
}
|
||||
|
||||
function f(x, cb) {
|
||||
called[x] = ++i;
|
||||
cb(null, x);
|
||||
}
|
||||
|
||||
function err_on(expected) {
|
||||
return function (err, result, context) {
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
if (result == expected) {
|
||||
return expected;
|
||||
}
|
||||
_("Got " + result + ", passing.");
|
||||
};
|
||||
}
|
||||
|
||||
// Fail in the middle.
|
||||
reset();
|
||||
Async.serially(["a", "b", "d"], f, err_on("b"), function (err) {
|
||||
do_check_eq(1, called["a"]);
|
||||
do_check_eq(2, called["b"]);
|
||||
do_check_false(!!called["d"]);
|
||||
do_check_eq(err, "b");
|
||||
|
||||
// Don't fail.
|
||||
reset();
|
||||
Async.serially(["a", "d", "b"], f, err_on("x"), function (err) {
|
||||
do_check_eq(1, called["a"]);
|
||||
do_check_eq(3, called["b"]);
|
||||
do_check_eq(2, called["d"]);
|
||||
do_check_false(!!err);
|
||||
|
||||
// Empty inputs.
|
||||
reset();
|
||||
Async.serially([], f, err_on("a"), function (err) {
|
||||
do_check_false(!!err);
|
||||
|
||||
reset();
|
||||
Async.serially(undefined, f, err_on("a"), function (err) {
|
||||
do_check_false(!!err);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_countedCallback() {
|
||||
let error = null;
|
||||
let output = null;
|
||||
let context = null;
|
||||
let counter = 0;
|
||||
function cb(err, result, ctx) {
|
||||
counter++;
|
||||
output = result;
|
||||
error = err;
|
||||
context = ctx;
|
||||
if (err == "error!")
|
||||
return "Oh dear.";
|
||||
}
|
||||
|
||||
let c1;
|
||||
|
||||
c1 = Async.countedCallback(cb, 3, function (err) {
|
||||
do_check_eq(2, counter);
|
||||
do_check_eq("error!", error);
|
||||
do_check_eq(2, output);
|
||||
do_check_eq("b", context);
|
||||
do_check_eq(err, "Oh dear.");
|
||||
|
||||
// If we call the counted callback again (once this output function is
|
||||
// done, that is), then the component callback is not invoked.
|
||||
Utils.nextTick(function () {
|
||||
_("Don't expect component callback.");
|
||||
c1("not", "running", "now");
|
||||
do_check_eq(2, counter);
|
||||
do_check_eq("error!", error);
|
||||
do_check_eq(2, output);
|
||||
do_check_eq("b", context);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
c1(1, "foo", "a");
|
||||
do_check_eq(1, counter);
|
||||
do_check_eq(1, error);
|
||||
do_check_eq("foo", output);
|
||||
do_check_eq("a", context);
|
||||
|
||||
c1("error!", 2, "b");
|
||||
// Subsequent checks must now take place inside the 'done' callback... read
|
||||
// above!
|
||||
});
|
||||
|
||||
add_test(function test_finallyCallback() {
|
||||
let fnCalled = false;
|
||||
let cbCalled = false;
|
||||
let error = undefined;
|
||||
|
||||
function reset() {
|
||||
fnCalled = cbCalled = false;
|
||||
error = undefined;
|
||||
}
|
||||
|
||||
function fn(arg) {
|
||||
do_check_false(!!arg);
|
||||
fnCalled = true;
|
||||
}
|
||||
|
||||
function fnThrow(arg) {
|
||||
do_check_false(!!arg);
|
||||
fnCalled = true;
|
||||
throw "Foo";
|
||||
}
|
||||
|
||||
function cb(next, err) {
|
||||
_("Called with " + err);
|
||||
cbCalled = true;
|
||||
error = err;
|
||||
next();
|
||||
}
|
||||
|
||||
function allGood(next) {
|
||||
reset();
|
||||
let callback = cb.bind(this, function() {
|
||||
do_check_true(fnCalled);
|
||||
do_check_true(cbCalled);
|
||||
do_check_false(!!error);
|
||||
next();
|
||||
});
|
||||
Async.finallyCallback(callback, fn)(null);
|
||||
}
|
||||
|
||||
function inboundErr(next) {
|
||||
reset();
|
||||
let callback = cb.bind(this, function() {
|
||||
do_check_true(fnCalled);
|
||||
do_check_true(cbCalled);
|
||||
do_check_eq(error, "Baz");
|
||||
next();
|
||||
});
|
||||
Async.finallyCallback(callback, fn)("Baz");
|
||||
}
|
||||
|
||||
function throwsNoErr(next) {
|
||||
reset();
|
||||
let callback = cb.bind(this, function() {
|
||||
do_check_true(fnCalled);
|
||||
do_check_true(cbCalled);
|
||||
do_check_eq(error, "Foo");
|
||||
next();
|
||||
});
|
||||
Async.finallyCallback(callback, fnThrow)(null);
|
||||
}
|
||||
|
||||
function throwsOverrulesErr(next) {
|
||||
reset();
|
||||
let callback = cb.bind(this, function() {
|
||||
do_check_true(fnCalled);
|
||||
do_check_true(cbCalled);
|
||||
do_check_eq(error, "Foo");
|
||||
next();
|
||||
});
|
||||
Async.finallyCallback(callback, fnThrow)("Bar");
|
||||
}
|
||||
|
||||
chain([throwsOverrulesErr,
|
||||
throwsNoErr,
|
||||
inboundErr,
|
||||
allGood]);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function run_test() {
|
||||
let input = [1, 2, 3, 4, 5];
|
||||
|
||||
let err;
|
||||
try {
|
||||
Utils.slices(input, 0).next();
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
do_check_eq("Invalid slice size.", err);
|
||||
|
||||
err = undefined;
|
||||
try {
|
||||
Utils.slices(input).next();
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
do_check_eq("Invalid slice size.", err);
|
||||
|
||||
function slurp(g) [x for each (x in g)]
|
||||
|
||||
let sliced1 = slurp(Utils.slices(input, 1));
|
||||
let sliced2 = slurp(Utils.slices(input, 2));
|
||||
let sliced3 = slurp(Utils.slices(input, 5));
|
||||
let sliced4 = slurp(Utils.slices(input, 7));
|
||||
|
||||
do_check_eq(sliced1.length, 5);
|
||||
do_check_eq(sliced2.length, 3);
|
||||
do_check_eq(sliced3.length, 1);
|
||||
do_check_eq(sliced4.length, 1);
|
||||
sliced1.every(function(x) x.length == 1);
|
||||
_(JSON.stringify(sliced2));
|
||||
do_check_eq(sliced2[0].length, 2);
|
||||
do_check_eq(sliced2[1].length, 2);
|
||||
do_check_eq(sliced2[2].length, 1);
|
||||
sliced3.every(function(x) x.length == 5);
|
||||
sliced4.every(function(x) x.length == 5);
|
||||
|
||||
let sliced5 = slurp(Utils.slices(["foo"], 50));
|
||||
do_check_eq(sliced5.length, 1);
|
||||
do_check_eq(sliced5[0], "foo");
|
||||
|
||||
let sliced6 = slurp(Utils.slices([], 50));
|
||||
do_check_eq(sliced6.length, 1);
|
||||
do_check_eq(sliced6[0].length, 0);
|
||||
}
|
|
@ -4,7 +4,6 @@ tail =
|
|||
|
||||
[test_Observers.js]
|
||||
[test_Preferences.js]
|
||||
[test_async_helpers.js]
|
||||
[test_async_querySpinningly.js]
|
||||
[test_auth_manager.js]
|
||||
[test_bookmark_batch_fail.js]
|
||||
|
@ -126,6 +125,5 @@ skip-if = (os == "mac" && debug) || os == "android"
|
|||
[test_utils_sha1.js]
|
||||
[test_utils_sha1hmac.js]
|
||||
[test_utils_sha256HMAC.js]
|
||||
[test_utils_slices.js]
|
||||
[test_utils_stackTrace.js]
|
||||
[test_utils_utf8.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче