Skip to content

Instantly share code, notes, and snippets.

@kyo-ago
Last active December 17, 2015 16:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyo-ago/5636416 to your computer and use it in GitHub Desktop.
Save kyo-ago/5636416 to your computer and use it in GitHub Desktop.

Obsolete

このGistは以下のリポジトリへ移動されました。
以後、ここは更新されません。

https://github.com/kyo-ago/jsdeferred-tiny

Deferred.js

forked from enchant.js/README.md at master · wise9/enchant.js · GitHub enchant.Deferred

安易版JSDeferred(enchant.Deferred)のテストをMocha+Chaiにしてtry catchをやめたもの

try catchをやめたのでstack traceがたどれます

失敗時にはnew DeferredStop('message');をreturnしてください(throwは単純にエラーになります

Deferred.js sandbox - js do it
jQueryも読み込んでいるので、$.ajax等と混ぜた使い方も確認できます。

Usage

基本的な使い方

var defer = new Deferred();

// (1) defer.call()が呼ばれた時に呼ばれるfunctionを登録
defer.next(function() {
  console.log(2);
});

setTimeout(function () {
	// (3) 上で登録したfunctionを呼び出す(console.log(2);が実行される)
	defer.call();
	// (4) defer.call();は同期的に実行されるのでその後ここが実行
	console.log(3);
});

// (2) setTimeout自体は遅延されるので、ここが先に実行
console.log(1);

jQuery.deferred風

var defer = new Deferred()
$.get('/', function (data) {
  defer.call(data);
});
defer.next(function (data) {
	console.log(data);
});

あとはテストケースのうち、「parallel Deferred #arrayArgument」と「parallel Deferred #objectArgument」が使えれば大丈夫だと思います。

License

MIT License

(function (global) {
"use strict";
var klass = function () {
this._succ = this._fail = this._next = this._id = null;
this._tail = this;
};
var prop = klass.prototype;
function DeferredStop (message) {
this.message = message;
}
prop.next = function (func) {
var q = new Deferred();
q._succ = func;
return this._add(q);
};
prop.error = function (func) {
var q = new Deferred();
q._fail = func;
return this._add(q);
};
prop._add = function (queue) {
this._tail._next = queue;
this._tail = queue;
return this;
};
prop.call = function (arg) {
var received;
var queue = this;
while (queue && !queue._succ) {
queue = queue._next;
}
if (!(queue instanceof Deferred)) {
return;
}
received = queue._succ(arg);
if (received instanceof DeferredStop) {
return queue.fail(received);
} else if (received instanceof Deferred) {
Deferred._insert(queue, received);
} else if (queue._next instanceof Deferred) {
queue._next.call(received);
}
};
prop.fail = function (arg) {
var result, err,
queue = this;
while (queue && !queue._fail) {
queue = queue._next;
}
if (queue instanceof DeferredStop) {
return;
} else if (queue instanceof Deferred) {
result = queue._fail(arg);
queue.call(result);
} else if (arg instanceof Error) {
throw arg;
} else {
err = new Error('failed in Deferred');
err.arg = arg;
throw err;
}
};
klass._insert = function(queue, ins) {
if (queue._next instanceof Deferred) {
ins._next = queue._next;
}
queue._next = ins;
};
klass.stop = function (message) {
return new DeferredStop(message);
};
klass.next = function(func) {
var q = new Deferred().next(func);
q._id = setTimeout(function() { q.call(); }, 0);
return q;
};
klass.parallel = function(arg) {
var p = new Deferred();
if (!arg) {
Deferred.next(function () { p.call(); });
return p;
}
var ret = (arg instanceof Array) ? [] : {};
var progress = 0;
for (var prop in arg) {
if (arg.hasOwnProperty(prop)) {
/*jshint loopfunc:true */
(function(queue, name) {
if (typeof queue === 'function') {
queue = Deferred.next(queue);
}
queue.next(function(arg) {
progress--;
ret[name] = arg;
if (progress === 0) {
p.call(ret);
}
})
.error(function(err) { p.fail(err); });
if (typeof queue._id === 'number') {
clearTimeout(queue._id);
}
queue._id = setTimeout(function() {
queue.call();
}, 0);
progress++;
}(arg[prop], prop));
}
}
if (!progress) {
Deferred.next(function () { p.call(); });
}
return p;
};
global['Deferred'] = klass;
global['DeferredStop'] = DeferredStop;
})(this);
describe('Deferred', function () {
before(function () {
this.clock = sinon.useFakeTimers();
});
after(function () {
this.clock && this.clock.restore();
});
it('new Deferred', function () {
var result = false;
var q = new Deferred().next(function() {
result = true;
});
expect(result).to.eql(false);
q.call();
expect(result).to.eql(true);
});
it('Deferred.next', function () {
var result = false;
Deferred.next(function() {
result = true;
});
expect(result).to.eql(false);
this.clock.tick(100);
expect(result).to.eql(true);
});
it('chained Deferred', function () {
var result1 = false, result2 = false, hoge;
Deferred.next(function() {
result1 = true;
return 'hoge';
})
.next(function(arg) {
result2 = true;
hoge = arg;
});
this.clock.tick(100);
expect(result1 & result2).to.eql(1);
expect(hoge).to.eql('hoge');
});
it('Deferred#error throw', function () {
expect(function() {
new Deferred().next(function() {
throw new Error('fail');
}).call();
}).to.throwException(/fail/);
});
it('Deferred#error1', function () {
var result1 = false, result2 = false;
Deferred.next(function() {
return new DeferredStop('fail');
})
.next(function() {
result1 = true;
})
.error(function() {
result2 = true;
});
this.clock.tick(100);
expect(result1).to.eql(false);
expect(result2).to.eql(true);
});
it('Deferred#error2', function () {
var result1 = false, result2 = false;
Deferred.next(function() {
return Deferred.stop('fail');
})
.next(function() {
result1 = true;
})
.error(function() {
result2 = true;
});
this.clock.tick(100);
expect(result1).to.eql(false);
expect(result2).to.eql(true);
});
it('async Deferred1', function () {
var result = false;
Deferred.next(function() {
var q = new Deferred();
setTimeout(function() {
q.call();
}, 100);
return q;
})
.next(function() {
result = true;
});
expect(result).to.eql(false);
this.clock.tick(200);
expect(result).to.eql(true);
});
it('async Deferred2', function () {
var result1 = false, result2 = false;
Deferred.next(function() {
var q = new Deferred();
setTimeout(function() {
q.fail();
}, 100);
return q;
})
.next(function() {
result1 = false;
})
.error(function() {
result2 = true;
});
expect(result1 | result2).to.eql(0);
this.clock.tick(200);
expect(result1).to.eql(false);
expect(result2).to.eql(true);
});
it('parallel Deferred #arrayArgument', function () {
Deferred.parallel([
Deferred.next(function() {
return 1;
}),
Deferred.next(function() {
return 2;
}),
Deferred.next(function() {
return 3;
})
])
.next(function(arr) {
expect(arr).to.eql([ 1, 2, 3 ]);
});
this.clock.tick(100);
});
it('parallel Deferred #objectArgument', function () {
Deferred.parallel({
one: Deferred.next(function() {
return 1;
}),
two: Deferred.next(function() {
return 2;
}),
three: Deferred.next(function() {
return 3;
})
})
.next(function(obj) {
expect(obj).to.eql({ one: 1, two: 2, three: 3 });
});
this.clock.tick(100);
});
it('parallel Deferred #Error', function () {
var result = false;
var c = 0;
Deferred.parallel([
Deferred.next(function() {
return new DeferredStop('error1');
}),
Deferred.next(function() {
return new DeferredStop('error2');
}),
Deferred.next(function() {
return 'success';
})
])
.next(function(arr) {
result = true;
})
.error(function(err) {
c++;
if (!(err instanceof Error) || !(/error\d/.test(err.message))) {
return new DeferredStop('');
}
});
this.clock.tick(100);
expect(result).to.eql(false);
expect(c).to.eql(2);
});
it('parallel Deferred next base', function () {
var stub = sinon.stub();
Deferred.parallel([
Deferred.next(function() {
var defer = new Deferred();
setTimeout(function () {
defer.call();
});
return defer;
}),
Deferred.next(function() {
var defer = new Deferred();
setTimeout(function () {
defer.call();
});
return defer;
})
])
.next(stub);
this.clock.tick(100);
expect(stub.callCount).to.eql(1);
});
it('parallel Deferred next pure function', function () {
var stub = sinon.stub();
Deferred.parallel([
function () {
var defer = new Deferred();
setTimeout(function () {
defer.call();
});
return defer;
},
function () {
var defer = new Deferred();
setTimeout(function () {
defer.call();
});
return defer;
}
])
.next(function () {
stub.call();
});
this.clock.tick(100);
expect(stub.callCount).to.eql(1);
});
it('parallel Deferred next empty arry', function () {
var stub = sinon.stub();
Deferred.parallel([])
.next(function () {
stub.call();
});
this.clock.tick(100);
expect(stub.callCount).to.eql(1);
});
it('parallel Deferred next nullable', function () {
var stub = sinon.stub();
Deferred.parallel()
.next(function () {
stub.call();
});
this.clock.tick(100);
expect(stub.callCount).to.eql(1);
});
it('parallel Deferred next parallel chain', function () {
var stub = sinon.stub();
var parallel1 = sinon.stub();
var parallel2 = sinon.stub();
Deferred.next(function () {
Deferred.parallel([function () {}]).next(parallel1);
}).next(function () {
Deferred.parallel([function () {}]).next(parallel2);
}).next(function () {
stub.call();
});
this.clock.tick(100);
expect(stub.calledBefore(parallel2)).to.ok();
expect(stub.calledBefore(parallel1)).to.ok();
expect(stub.callCount).to.eql(1);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment