Closed axetroy closed 5 years ago
So defer is simply an helper for :
await new Promise(res => {
await RemoveFile(filename);
res();
});
Right?
@zekth Not at all. @ry should know that.
here is the example
test(async ({ defer }) => {
console.log("do first job");
defer(async () => {
console.log("1");
});
console.log("do second job");
defer(async () => {
console.log("2");
});
console.log("do third job");
defer(async () => {
console.log("3");
});
console.log("job done.");
});
// print out
// do first job
// do second job
// do third job
// job done.
// 3
// 2
// 1
I get it. Using the same defer behaviour LIFO after all the sync code has been executed.
Maybe call it cleanup()
?
I don’t like the idea because it needs to be called in a special context to work. (Unless I’m missing something?) It’s impossible to implement the general semantics that Go has.
@ry In fact, we only need to create a context containing the defer function when execute each test function.
After the test function is executed (regardless of reject or resolve), then the defer
queue is executed one by one.
This is achievable.
and it can be No side effects
, No breaking API
Looking back, it can also add hooks like after
test(async ({ defer, after }) => {
after(async () => {
console.log('do the job after this test finish');
});
});
@axetroy But it only works in test, and the name suggests it can work elsewhere... If we could implement this generally, I'd be all for it - but I think that would be impossible without a compilation pass.
I am not sure about the semantics of Go, but Promises are already scheduled out of turn in JavaScript as a microtask. So all this would be is a function that would reschedule at the end of the current queue before the next macrotask. That should be implementable just in the runtime by creating a new Promise.
In Go, the deferred function is executed before the function returns to caller, but after the body of the function is executed.
I haven't really thought thru how to implement this, but it seems not possible... If @axetroy has a way to do this in general, I'd be all for adding it. (It's super useful and a very nice feature.) But if it's restricted to test()
callbacks, I think we should not do it.
I haven't really thought thru how to implement this, but it seems not possible...
One way, if there is a compelling use case, would be that the test
function would always pass a function named deferred(cb: () => void | Promise<void>)
which could then could schedule/drain the deferreds before the test function returns, as @axetroy indicates. Usage would be something like this:
test(function testSomething({ defer }) {
defer(() => console.log("b"));
console.log("a");
});
Anything throwing in there would be attributed to the test function. No magic, just "standard" JavaScript.
After I finish this #261, I will do a PR.
@ry I have implemented this feature.
try it out
save as defer.ts
file
deno defer.ts
type DeferFunc = () => Promise<void>;
type Defer = (fn: DeferFunc) => void;
interface Context {
defer: Defer;
}
type func<T> = (context: Context) => Promise<T>;
function deferify<T>(fn: func<T>) {
return async function(): Promise<T> {
const defers: DeferFunc[] = [];
const context: Context = {
defer(fn) {
defers.push(fn);
return;
},
};
async function dequeue() {
while (defers.length) {
const deferFn = defers.pop();
if (deferFn) {
try {
await deferFn();
} catch {}
}
}
}
return fn(context)
.then((r: any) => {
return dequeue().then(() => r);
})
.catch((err: Error) => {
return dequeue().then(() => Promise.reject(err));
});
};
}
deferify(async ({ defer }) => {
console.log('do first job');
defer(async () => {
console.log('1');
});
console.log('do second job');
defer(async () => {
console.log('2');
});
console.log('do third job');
defer(async () => {
console.log('3');
});
console.log('job done.');
})();
// do first job
// do second job
// do third job
// job done.
// 3
// 2
// 1
If you think this is ok, can you reopen the issue?
@axetroy Sorry - it's a bit too non-standard and boilerplate-y for me. Please submit it to https://github.com/denoland/registry
defer
in Golang is easy is useful especially in the test.eg.
do the same thing in
Deno
I wrote a library before. https://github.com/axetroy/godefer