inspired by signals.js