`Promise` support
kl0tl opened this issue · 7 comments
Would be great to support returning a promise to end the test when the promise is resolved.
- It would play nice with future extensions of the language, allowing one to call
tape
with Async Functions to transparently return a promise. - It would eliminate some otherwise unfixable ordering issues with nested tests.
- Rewriting
var test = require('tape');
test('first', function (t) {
setTimeout(function () {
t.ok(1, 'first test');
t.end();
}, 200);
t.test('second', function (t) {
t.ok(1, 'second test');
t.end();
});
});
test('third', function (t) {
setTimeout(function () {
t.ok(1, 'third test');
t.end();
}, 100);
});
to (assuming a function delay
returning a Promise
resolved after t
milliseconds)
var test = require('tape');
test('first', async function (t) {
await delay(200);
t.ok(1, 'first test');
t.test('second', async function (t) {
t.ok(1, 'second test');
});
});
test('third', async function (t) {
await delay(100);
t.ok(1, 'third test');
});
will preserve the expected order (see #222).
Looking at blue-tape it should be doable with something along the lines of
var ret = this._cb(this);
var self = this;
if (ret && typeof ret.then === 'function') {
ret.then(function () {
self.end();
}, function (err) {
nextTick(function () {
throw err;
});
});
}
Note that it would not require a Promise
library nor any configuration. Users wanting to take advantage of this feature would need to provide their own Promise
implementation if necessary anyway.
From #222 (comment):
Supporting that is tricky because we'd have to either a) assume Promise was available, which forces the user to know they need to shim it, b) allow explicit injection of
Promise
so they can pass bluebird or native Promises or whatever as they like (like.setPromiseConstructor()
or something), or c) add a Promise lib as a dependency.Solution a is dangerous, b is awkward, and c is heavyweight.
The problem is that tape
would need to be able to wrap test return values in Promise.resolve
since it's a bad idea to do an "is this a promise" check - which means tape
needs to be able to know that Promise
is available.
Why would wrapping be necessary ? We don’t need any of the capabilities of well behaved promises here. The success callback could even be called on the same tick and tests would still close properly.
and if current tests return non-promise objects?
Absolutely we do need them - those capabilities (including error-catching) are part of the guarantee of promises. If the success callback is called on the same tick than a previously set-up "next tick" operation might not fire, and the test would be invalid.
Yeah I know about the guarantees promises offer but I think it's unlikely something too bad could happen here. Anyway, couldn't this behavior be namespaced under a new Test#async
method to make the intent explicit ? Would it be acceptable to trust users to provide well behaved enough promises in that case ?
Doesn't tape
do let errors propagate on purpose ? I'm not sure what you're meaning about error catching.
This would make tape much more complicated. A promise-oriented test library should be a separate project. Tape is a callback-oriented project. I would much rather there were 2 projects focusing on separate, clear conceptions of what they are trying to be than a single monster of a project that mixes a bunch of ideas into an incoherent mess.
@substack It's unfortunate to hold onto this callback based API, it seems to be the opposite of the direction the language is taking and its causing some weird issues. Anyway, thanks for taking the time to talk !
Time to move to promises?