├── .gitignore ├── README.md ├── complexity.js ├── debuggability.js ├── examples-extra ├── fibby.js ├── promises-bluebird-bind-ctx.js ├── promises-bluebird-bind.js ├── promises-bluebird-generator-ctx-nocatch.js └── promises.js ├── examples-redis-chaining ├── callbacks-async-waterfall.js ├── promises-bluebird.js ├── promises-kew.js └── promises-q.js ├── examples-redis ├── callbacks.js ├── promises-bluebird.js ├── promises-kew.js └── promises-q.js ├── examples ├── callbacks-async-waterfall.js ├── callbacks-catcher.js ├── callbacks-deferred-queue.js ├── callbacks-flattened-class-ctx.js ├── callbacks-flattened-class.js ├── callbacks-flattened-passing.js ├── callbacks-flattened.js ├── callbacks-generator-genny.js ├── callbacks-generator-suspend.js ├── callbacks-original.js ├── dst-callbacks-generator-genny-traceur.js ├── dst-callbacks-generator-suspend-traceur.js ├── dst-promises-q-generator-traceur.js ├── dst-stratifiedjs-compiled.js ├── dst-streamline-fibers.js ├── dst-streamline.js ├── dst-thunks-generator-co-traceur.js ├── fibrous.js ├── promises-bluebird-ctx.js ├── promises-bluebird-generator-ctx.js ├── promises-bluebird-generator.js ├── promises-bluebird-spawn.js ├── promises-bluebird.js ├── promises-compose-bluebird.js ├── promises-compose-p.js ├── promises-compose-q.js ├── promises-kew.js ├── promises-p.js ├── promises-q-generator.js ├── promises-q.js ├── promises-tildeio-rsvp.js ├── promises-when.js ├── rx.js ├── src-stratifiedjs.sjs ├── src-streamline._js ├── thunks-generator-co.js └── thunks-generator-gens.js ├── latest-results.md ├── lib ├── catcher.js ├── dummy.js ├── fakemaker.js ├── fakes-ctx.js ├── fakes.js ├── fakesC.js ├── fakesO.js ├── fakesP-ctx.js ├── fakesP.js ├── fakesSJS-dst.js ├── fakesSJS-src.sjs ├── kew-lifter.js ├── promiseSupport.js └── timers-ctx.js ├── package.json └── performance.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-compare 2 | 3 | This project aims to compare various node.js async patterns by their 4 | 5 | - complexity (number of necessary tokens) 6 | - performance when executing in parallel (time and memory) 7 | - debuggability 8 | 9 | The resulting analysis is available at 10 | [this blog post](http://spion.github.io/posts/analysis-generators-and-other-async-patterns-node.html) 11 | 12 | ## example problem 13 | 14 | The problem is directly extracted from a DoxBee project. Its a typical if 15 | somewhat complex CRUD method executed when a user uploads a new document 16 | to the database. It involves multiple queries to the database, a couple of 17 | selects, some inserts and one update. Lots of mixed sync/async action. 18 | 19 | ## files 20 | 21 | Example solutions for all patterns are located in the `examples` directory 22 | 23 | Non-js sorce files begin with `src-` (they're not checked for performance) 24 | 25 | Compiled files are prefixed with `dst-` (they're not checked for complexity) 26 | 27 | All other files are checked for both performance and complexity 28 | 29 | ## complexity 30 | 31 | Complexity is measured by the number of tokens in the source code found by 32 | Esprima's lexer (comments excluded) 33 | 34 | Run `node complexity.js` to get complexity reports for all files. 35 | 36 | 37 | ## fakes.js 38 | 39 | Wrappers can be added in `lib/fakes.js` 40 | 41 | For examples, look at the promise and thunk wrappers for query methods. 42 | 43 | Things that are specific to the upload function are not allowed here. 44 | 45 | 46 | ## performance 47 | 48 | All external methods are mocked with setTimeout, to simulate waiting for I/O 49 | operations. 50 | 51 | Performance is measured by performance.js 52 | 53 | node performance.js --n --t ./examples/*.js --harmony 54 | 55 | where `n` is the number of parallel executions of the method, while `t` is the 56 | time each simulated I/O operation should take, and `--harmony` enables 57 | all features hidden behind the v8 flag. 58 | 59 | There is an optional parameter `--file ` which will only test a single 60 | file and report any encountered errors in detail: 61 | 62 | node --harmony performance.js --n 10000 --t 10 --file ./examples/genny.js 63 | 64 | Also, this variant doesn't spawn a new process so which means additional 65 | (v8) options can be passed to node. 66 | 67 | If you omit `--n`, tests will be made with 100, 500, 1000 and 2000 parallel 68 | requests and a giant JSON report (suitable for charts) will be generated. 69 | 70 | node performance.js --t 1 ./examples/*.js --harmony 71 | 72 | If you omit `--n` *and* replace `--t` with `--dt`, I/O time `t` will grow with 73 | `n` by the formula `t = n * dt` 74 | 75 | node performance.js --dt 0.1 ./examples/*.js --harmony 76 | 77 | Execution time and peak memory usage are reported. 78 | 79 | 80 | ## debuggability 81 | 82 | 83 | `debuggability.js` measures the distance between the function that creates the 84 | error and the actual error in the stack trace. Reports "-" at places where 85 | the stack trace is completely missing the original file. 86 | 87 | To check all examples for async errors: 88 | 89 | ``` 90 | node debuggability.js --harmony --error 91 | ``` 92 | 93 | and for exceptions: 94 | 95 | ``` 96 | node debuggability.js --harmony --throw 97 | ``` 98 | 99 | and finally for exceptions inside async calls (most things can't handle this): 100 | 101 | 102 | ``` 103 | node debuggability.js --harmony --athrow 104 | ``` 105 | 106 | 107 | ## misc 108 | 109 | These are factors potentially important for collaboration which could 110 | be added as points to arrive at a final score: 111 | 112 | - does it require native modules (-2) 113 | - does it require code transformation (-2) 114 | - will it eventually become available without code transformation (+1) 115 | 116 | 117 | -------------------------------------------------------------------------------- /complexity.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'); 3 | var table = require('text-table'); 4 | 5 | var stats = module.exports = function stats() { 6 | return fs.readdirSync(__dirname + '/examples').filter(function(f){ 7 | return !/^dst-/.test(f); 8 | }).map(function(f) { 9 | var file = fs.readFileSync('./examples/'+f).toString(); 10 | file = file.replace(/function\s*\*/g, 'function') 11 | .replace(/yield/g, 'void'); 12 | try { 13 | var tree = require('esprima').parse(file, { 14 | tolerant: true, 15 | tokens: true 16 | }); 17 | } catch (e) { 18 | console.log("In file", f, ":"); 19 | console.log(e); 20 | } 21 | return {name: f, tokens: tree.tokens.length} 22 | }); 23 | } 24 | 25 | var s = stats(); 26 | 27 | var mintokens = s.reduce(function(acc, f) { 28 | return Math.min(acc, f.tokens); 29 | }, Number.POSITIVE_INFINITY); 30 | 31 | s = s.sort(function(s1, s2) { 32 | return s1.tokens - s2.tokens; 33 | }); 34 | 35 | s.forEach(function(f) { 36 | f.complexity = f.tokens / mintokens; 37 | }); 38 | 39 | console.log(table([['name', 'tokens', 'complexity']].concat( 40 | s.map(function(f) { return [f.name, f.tokens, f.complexity.toFixed(2)] }) 41 | ), {align: ['l','r','r']})); 42 | 43 | -------------------------------------------------------------------------------- /debuggability.js: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = Infinity; 2 | 3 | var args = require('optimist').argv; 4 | 5 | global.longStackSupport = require('q').longStackSupport = true; 6 | require("p-promise").longStackSupport = true; 7 | require("bluebird").longStackTraces(); 8 | 9 | var path = require('path'); 10 | 11 | var perf = module.exports = function(args, done) { 12 | global.asyncTime = args.t || 1; 13 | global.testThrow = args.throw; 14 | global.testThrowAsync = args.athrow; 15 | global.testError = args.error; 16 | 17 | var fn = require(args.file); 18 | fn('a','b','c', done); 19 | } 20 | 21 | 22 | if (args.file) { 23 | perf(args, function(err) { 24 | if (err) { 25 | // for browser-compatibility, stratifiedjs reports errors 26 | // on __oni_stack (or toString()), rather than 'stack'. 27 | 28 | var stratifiedStack = err.__oni_stack && 29 | err.__oni_stack.map(function(x) { 30 | return x.join(':') }).join('\n'); 31 | 32 | var callbackStack = new Error().stack 33 | .split('\n').slice(1).join('\n'); 34 | console.error( 35 | stratifiedStack || 36 | err.stack + '\nCallback stack:\n' + callbackStack); 37 | } 38 | }); 39 | } else { 40 | var cp = require('child_process') 41 | var async = require('async'); 42 | var fs = require('fs'); 43 | var dir = path.join(__dirname, 'examples'); 44 | 45 | var table = require('text-table'); 46 | 47 | 48 | var allfiles = fs.readdirSync(dir); 49 | 50 | var files = allfiles.filter(function(f) { 51 | return !/^src-/.test(f); 52 | }); 53 | 54 | var sources = allfiles.filter(function(f) { 55 | return !/^dst-/.test(f); 56 | }); 57 | 58 | var sourceOf = function(f) { 59 | if (!/^dst-/.test(f)) return f; 60 | var name = f.replace(/^dst-/, '').replace(/-[^-]+.js$/, ''); 61 | return sources.filter(function(s) { 62 | return s.indexOf(name) >= 0; 63 | })[0] || f; 64 | } 65 | 66 | async.mapSeries(files, function(f, done) { 67 | console.error("testing", f); 68 | 69 | var argsFork = [__filename, 70 | '--file', path.join(dir, f)]; 71 | if (args.error) argsFork.push('--error') 72 | if (args.throw) argsFork.push('--throw'); 73 | if (args.athrow) argsFork.push('--athrow'); 74 | 75 | if (args.harmony) argsFork.unshift('--harmony'); 76 | 77 | var p = cp.spawn(process.execPath, argsFork); 78 | 79 | 80 | var lineNumber = -1; 81 | 82 | (function(){ 83 | var lines = fs.readFileSync(path.join(dir, sourceOf(f)), 'utf8').split('\n'); 84 | var sawFileVersionInsert = false; 85 | for (var i = 0, len = lines.length; i < len; ++i) { 86 | var item = lines[i]; 87 | if (!sawFileVersionInsert) { 88 | if (item.indexOf('FileVersion.insert') >= 0) { 89 | sawFileVersionInsert = true; 90 | //The .execWithin could be on the same line 91 | if (item.indexOf('execWithin') > 92 | item.indexOf('FileVersion.insert')) { 93 | lineNumber = i + 1; 94 | break; 95 | } 96 | } 97 | } 98 | else { 99 | if (item.indexOf('execWithin') >= 0) { 100 | lineNumber = i + 1; 101 | break; 102 | } 103 | } 104 | } 105 | if (lineNumber < 0) { 106 | throw new Error("Example didn't contain throwing line: " 107 | + sourceOf(f)); 108 | } 109 | })(); 110 | var r = { file: f, data: [], line: lineNumber }; 111 | var separator = require("path").sep; 112 | p.stderr.pipe(process.stderr); 113 | p.stderr.on('data', function(d) { r.data.push(d.toString());}); 114 | p.on('exit', function(code, second) { 115 | console.log("exit code", code, second); 116 | //p.stderr.on('end', function(code) { 117 | r.data = r.data.join('').split('\n').filter(function(line) { 118 | // match lines reporting either compiled or source files: 119 | return line.indexOf('examples\\' + f) >= 0 || 120 | line.indexOf('examples\\' + sourceOf(f)) >= 0 || 121 | line.indexOf('examples/' + f) >= 0 || 122 | line.indexOf('examples/' + sourceOf(f)) >= 0 123 | 124 | }).map(function(l) { 125 | //Windows dirs has colons in drives like "C:\" 126 | //so splitting on colon doesn't work :P 127 | var rlineno = /(?:\.js|\.sjs|\._js):(\d+)/; 128 | 129 | var line = rlineno.exec(l)[1]; 130 | return { 131 | content: l, 132 | line: line, 133 | distance: Math.abs(line - r.line) 134 | }; 135 | }).sort(function(l1, l2) { 136 | return l1.distance - l2.distance; 137 | }); 138 | 139 | 140 | r.data = r.data[0]; 141 | r.crashed = !!code; 142 | done(null, r); 143 | }); 144 | }, function(err, res) { 145 | 146 | console.log(""); 147 | console.log("error reporting"); 148 | console.log(""); 149 | res = res.sort(function(r1, r2) { 150 | var ret = r1.crashed - r2.crashed; 151 | if (ret === 0) 152 | ret = parseFloat(r1.data ? r1.data.distance : Infinity) 153 | - parseFloat(r2.data ? r2.data.distance : Infinity); 154 | 155 | if( ret === 0 ) { 156 | ret = r1.file < r2.file ? -1 : 157 | r1.file > r2.file ? 1 : 158 | 0; 159 | } 160 | return ret; 161 | }); 162 | res = res.map(function(r) { 163 | return [r.file, r.line, 164 | r.data ? r.data.line : '-', 165 | r.data ? r.data.distance : '-', 166 | r.crashed ? 'yes' : 'no']; 167 | }) 168 | res = [['file', 'actual-line', 'rep-line', 'distance', 'crashed']].concat(res) 169 | console.log(table(res, {align: ['l','r','r','r', 'r']})); 170 | }); 171 | } 172 | -------------------------------------------------------------------------------- /examples-extra/fibby.js: -------------------------------------------------------------------------------- 1 | var fibby = require('fibby'); 2 | 3 | module.exports = fibby.fn(function upload(f, stream, idOrPath, tag) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | try { 7 | var blobId = f.yield(blob.put(stream, f.resume.t)); 8 | var file = f.yield(self.byUuidOrPath(idOrPath).get(f.resume.t)); 9 | var previousId = file ? file.version : null; 10 | var version = { 11 | userAccountId: userAccount.id, 12 | date: new Date(), 13 | blobId: blobId, 14 | creatorId: userAccount.id, 15 | previousId: previousId, 16 | mergedId: null, 17 | mergeType: 'mine', 18 | comment: '', 19 | tag: tag 20 | }; 21 | version.id = Version.createHash(version); 22 | f.yield(Version.insert(version).execWithin(tx, f.resume.t)); 23 | if (!file) { 24 | var splitPath = idOrPath.split('/'); 25 | var fileName = splitPath[splitPath.length - 1]; 26 | var newId = uuid.v1(); 27 | var file = { 28 | id: newId, 29 | userAccountId: userAccount.id, 30 | type: 'file', 31 | name: fileName, 32 | version: version.id 33 | } 34 | var q = f.yield(self.createQuery(idOrPath, file, f.resume.t)); 35 | f.yield(q.execWithin(tx, f.resume.t)); 36 | } 37 | f.yield(FileVersion 38 | .insert({fileId: file.id,versionId: version.id}) 39 | .execWithin(tx, f.resume.t)); 40 | 41 | f.yield(File 42 | .whereUpdate({id: file.id}, {version: version.id}) 43 | .execWithin(tx, f.resume.t)); 44 | f.yield(tx.commit(f.resume.t)); 45 | } catch (err) { 46 | tx.rollback(); 47 | throw err; 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /examples-extra/promises-bluebird-bind-ctx.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | require('../lib/fakesP-ctx'); 5 | 6 | module.exports = function upload(stream, idOrPath, tag, done) { 7 | var blob = blobManager.create(account); 8 | var tx = db.begin(); 9 | 10 | bluebird.join( 11 | blob.put(stream), 12 | self.byUuidOrPath(idOrPath).get() 13 | ).bind({ 14 | done: done, 15 | stream: stream, 16 | idOrPath: idOrPath, 17 | tag: tag, 18 | version: void 0, 19 | fileId: void 0, 20 | file: void 0, 21 | tx: tx, 22 | newId: void 0, 23 | blob: blob 24 | }) 25 | .spread(step1) 26 | .then(step2) 27 | .then(step3) 28 | .then(step4) 29 | .then(step5) 30 | .catch(err); 31 | } 32 | 33 | function step1( blobId, file ) { 34 | this.file = file; 35 | var previousId = file ? file.version : null; 36 | var version = this.version = { 37 | userAccountId: userAccount.id, 38 | date: new Date(), 39 | blobId: blobId, 40 | creatorId: userAccount.id, 41 | previousId: previousId 42 | }; 43 | version.id = Version.createHash(version); 44 | return Version.insert(version).execWithin(this.tx); 45 | } 46 | 47 | function step2() { 48 | if (!this.file) { 49 | var splitPath = this.idOrPath.split('/'); 50 | var fileName = splitPath[splitPath.length - 1]; 51 | var newId = this.newId = uuid.v1(); 52 | return self.createQuery(this.idOrPath, { 53 | id: newId, 54 | userAccountId: userAccount.id, 55 | name: fileName, 56 | version: this.version.id 57 | }).bind(this).then(step2$1).then(step2$2); 58 | } else { 59 | return this.file.id; 60 | } 61 | } 62 | 63 | function step2$1(q) { 64 | return q.execWithin(this.tx); 65 | } 66 | 67 | function step2$2() { 68 | return this.newId; 69 | } 70 | 71 | function step3(fileId) { 72 | this.fileId = fileId; 73 | return FileVersion.insert({ 74 | fileId: fileId, 75 | versionId: this.version.id 76 | }).execWithin(this.tx); 77 | } 78 | 79 | function step4() { 80 | return File.whereUpdate( 81 | {id: this.fileId}, 82 | {version: this.version.id} 83 | ).execWithin(this.tx); 84 | } 85 | 86 | function step5() { 87 | this.tx.commit(); 88 | return this.done(); 89 | } 90 | 91 | function err(e) { 92 | this.tx.rollback(); 93 | return this.done(e); 94 | } 95 | -------------------------------------------------------------------------------- /examples-extra/promises-bluebird-bind.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | require('../lib/fakesP'); 5 | 6 | module.exports = function upload(stream, idOrPath, tag, done) { 7 | var blob = blobManager.create(account); 8 | var tx = db.begin(); 9 | 10 | bluebird.join( 11 | blob.put(stream), 12 | self.byUuidOrPath(idOrPath).get() 13 | ).bind({ 14 | done: done, 15 | stream: stream, 16 | idOrPath: idOrPath, 17 | tag: tag, 18 | version: void 0, 19 | fileId: void 0, 20 | file: void 0, 21 | tx: tx, 22 | newId: void 0, 23 | blob: blob 24 | }) 25 | .spread(step1) 26 | .then(step2) 27 | .then(step3) 28 | .then(step4) 29 | .then(step5) 30 | .catch(err); 31 | } 32 | 33 | function step1( blobId, file ) { 34 | this.file = file; 35 | var previousId = file ? file.version : null; 36 | var version = this.version = { 37 | userAccountId: userAccount.id, 38 | date: new Date(), 39 | blobId: blobId, 40 | creatorId: userAccount.id, 41 | previousId: previousId 42 | }; 43 | version.id = Version.createHash(version); 44 | return Version.insert(version).execWithin(this.tx); 45 | } 46 | 47 | function step2() { 48 | if (!this.file) { 49 | var splitPath = this.idOrPath.split('/'); 50 | var fileName = splitPath[splitPath.length - 1]; 51 | var newId = this.newId = uuid.v1(); 52 | return self.createQuery(this.idOrPath, { 53 | id: newId, 54 | userAccountId: userAccount.id, 55 | name: fileName, 56 | version: this.version.id 57 | }).bind(this).then(step2$1).then(step2$2); 58 | } else { 59 | return this.file.id; 60 | } 61 | } 62 | 63 | function step2$1(q) { 64 | return q.execWithin(this.tx); 65 | } 66 | 67 | function step2$2() { 68 | return this.newId; 69 | } 70 | 71 | function step3(fileId) { 72 | this.fileId = fileId; 73 | return FileVersion.insert({ 74 | fileId: fileId, 75 | versionId: this.version.id 76 | }).execWithin(this.tx); 77 | } 78 | 79 | function step4() { 80 | return File.whereUpdate( 81 | {id: this.fileId}, 82 | {version: this.version.id} 83 | ).execWithin(this.tx); 84 | } 85 | 86 | function step5() { 87 | this.tx.commit(); 88 | return this.done(); 89 | } 90 | 91 | function err(e) { 92 | this.tx.rollback(); 93 | return this.done(e); 94 | } 95 | -------------------------------------------------------------------------------- /examples-extra/promises-bluebird-generator-ctx-nocatch.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | require('../lib/fakesP-ctx'); 5 | 6 | module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { 7 | var blob = blobManager.create(account); 8 | var tx = db.begin(); 9 | //console.log("1"); 10 | var blobId = yield blob.put(stream); 11 | //console.log("2"); 12 | var file = yield self.byUuidOrPath(idOrPath).get(); 13 | //console.log("3"); 14 | 15 | var previousId = file ? file.version : null; 16 | version = { 17 | userAccountId: userAccount.id, 18 | date: new Date(), 19 | blobId: blobId, 20 | creatorId: userAccount.id, 21 | previousId: previousId, 22 | }; 23 | version.id = Version.createHash(version); 24 | yield Version.insert(version).execWithin(tx); 25 | if (!file) { 26 | var splitPath = idOrPath.split('/'); 27 | var fileName = splitPath[splitPath.length - 1]; 28 | file = { 29 | id: uuid.v1(), 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | } 34 | var query = yield self.createQuery(idOrPath, file); 35 | yield query.execWithin(tx); 36 | } 37 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 38 | .execWithin(tx); 39 | yield File.whereUpdate({id: file.id}, {version: version.id}) 40 | .execWithin(tx); 41 | tx.commit(); 42 | done(); 43 | }); 44 | -------------------------------------------------------------------------------- /examples-extra/promises.js: -------------------------------------------------------------------------------- 1 | var when = require('when'), 2 | fn = require('when/function'), 3 | p = require('../lib/promiseSupport.js'); 4 | 5 | 6 | require('../lib/fakesP'); 7 | 8 | // Assume Version = objectLift(version), File = objectLift(file) etc. 9 | module.exports = function upload(stream, idOrPath, tag, done) { 10 | var blob = blobManager.create(account); 11 | var tx = db.begin(); 12 | var blobIdP = blob.put(stream); 13 | var fileP = self.byUuidOrPath(idOrPath).get(); 14 | var previousIdP = p.ternary(fileP, p.get(fileP, 'version'), null); 15 | var versionP = p.allObject({ 16 | userAccountId: userAccount.id, 17 | date: new Date(), 18 | blobId: blobIdP, 19 | creatorId: userAccount.id, 20 | previousId: previousIdP, 21 | }); 22 | versionP = p.set(versionP, p.allObject({ 23 | id: fn.call(Version.createHash, versionP) 24 | })); 25 | // Even if Version.insert has been lifted, it returns a promise and 26 | // therefore we cannot call execWithin. We have to wait for the promise 27 | // to resolve 28 | var versionInsert = p.eventuallyCall( 29 | Version.insert(versionP), 'execWithin', tx); 30 | var versionIdP = p.get(versionP, 'id'); 31 | var fileIdP = p.if (p.not(fileP), function () { 32 | var splitPath = idOrPath.split('/'); 33 | var fileName = splitPath[splitPath.length - 1]; 34 | var newId = uuid.v1(); 35 | p.eventuallyCall(self.createQuery(idOrPath, p.allObject({ 36 | id: newId, 37 | userAccountId: userAccount.id, 38 | name: fileName, 39 | version: versionIdP 40 | }), 'execWithin', tx)).then(function() { 41 | return newId; 42 | }); 43 | }, function() { 44 | return p.get(fileP, 'id'); 45 | }); 46 | var addFileVersion = p.eventuallyCall( 47 | FileVersion.insert(p.allObject({ 48 | fileId: fileIdP, 49 | versionId: versionIdP 50 | })), 'execWithin', tx); 51 | var fileUpdate = p.eventuallyCall(File.whereUpdate( 52 | p.allObject({ id: fileIdP }), 53 | p.allObject({ version: p.get(versionP, 'id') })), 54 | 'execWithin', tx); 55 | when.all([versionInsert, addFileVersion, fileUpdate]).then(function() { 56 | tx.commit(); 57 | return done(); 58 | }, function(err) { 59 | tx.rollback(); 60 | return done(new Error(err)); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /examples-redis-chaining/callbacks-async-waterfall.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | var async = require('async'); 10 | 11 | var getfn = function(id, cb) { 12 | return cl.get(id, cb); 13 | } 14 | 15 | module.exports = function upload(stream, idOrPath, tag, done) { 16 | async.waterfall([ 17 | function(done) { getfn('bench-'+ (stream&255), done); }, 18 | getfn, 19 | getfn, 20 | getfn, 21 | getfn 22 | ], done); 23 | } 24 | 25 | module.exports.end = function() { 26 | cl.end(); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /examples-redis-chaining/promises-bluebird.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'bench-'+(k+1)); 12 | } 13 | */ 14 | 15 | 16 | bluebird.promisifyAll(cl); 17 | 18 | module.exports = function upload(stream, idOrPath, tag, done) { 19 | cl.getAsync('bench-'+(stream & 255)) 20 | .then(function(res) { 21 | return cl.getAsync(res); 22 | }) 23 | .then(function(res) { 24 | return cl.getAsync(res); 25 | }) 26 | .then(function(res) { 27 | return cl.getAsync(res); 28 | }).nodeify(done); 29 | } 30 | 31 | module.exports.end = function() { 32 | cl.end(); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /examples-redis-chaining/promises-kew.js: -------------------------------------------------------------------------------- 1 | global.useQ = true; 2 | 3 | var q = require('kew'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'some value contained'); 12 | } 13 | */ 14 | 15 | function bind(fn, ctx) { 16 | return function(a1, a2, a3, a4) { 17 | return fn.call(ctx, a1, a2, a3, a4); 18 | } 19 | } 20 | function nodeback(err, res) { 21 | if (err) this.reject(err); 22 | else this.resolve(res); 23 | } 24 | 25 | cl.getAsync = function(id) { 26 | var def = q.defer() 27 | cl.get(id, bind(nodeback, def)); 28 | return def.promise; 29 | } 30 | 31 | module.exports = function upload(stream, idOrPath, tag, done) { 32 | cl.getAsync('bench-'+(stream & 255)) 33 | .then(function(res) { 34 | return cl.getAsync(res); 35 | }) 36 | .then(function(res) { 37 | return cl.getAsync(res); 38 | }) 39 | .then(function(res) { 40 | return cl.getAsync(res); 41 | }) 42 | .then(function(ok) { done(null, ok) }, done); 43 | } 44 | 45 | module.exports.end = function() { 46 | cl.end(); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /examples-redis-chaining/promises-q.js: -------------------------------------------------------------------------------- 1 | global.useQ = true; 2 | 3 | var q = require('q'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'some value contained'); 12 | } 13 | */ 14 | 15 | cl.getAsync = q.nbind(cl.get, cl); 16 | 17 | module.exports = function upload(stream, idOrPath, tag, done) { 18 | cl.getAsync('bench-'+(stream & 255)) 19 | .then(function(res) { 20 | return cl.getAsync(res); 21 | }) 22 | .then(function(res) { 23 | return cl.getAsync(res); 24 | }) 25 | .then(function(res) { 26 | return cl.getAsync(res); 27 | }).nodeify(done); 28 | } 29 | 30 | module.exports.end = function() { 31 | cl.end(); 32 | } 33 | 34 | -------------------------------------------------------------------------------- /examples-redis/callbacks.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | 10 | module.exports = function upload(stream, idOrPath, tag, done) { 11 | cl.get('bench-'+(stream & 511), done); 12 | } 13 | 14 | module.exports.end = function() { 15 | cl.end(); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /examples-redis/promises-bluebird.js: -------------------------------------------------------------------------------- 1 | global.useBluebird = true; 2 | global.useQ = false; 3 | var bluebird = require('bluebird'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'some value contained'); 12 | } 13 | */ 14 | bluebird.promisifyAll(cl); 15 | 16 | module.exports = function upload(stream, idOrPath, tag, done) { 17 | cl.getAsync('bench-'+(stream & 511)).nodeify(done); 18 | } 19 | 20 | module.exports.end = function() { 21 | cl.end(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples-redis/promises-kew.js: -------------------------------------------------------------------------------- 1 | global.useQ = true; 2 | 3 | var q = require('kew'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'some value contained'); 12 | } 13 | */ 14 | 15 | function bind(fn, ctx) { 16 | return function(a1, a2, a3, a4) { 17 | return fn.call(ctx, a1, a2, a3, a4); 18 | } 19 | } 20 | function nodeback(err, res) { 21 | if (err) this.reject(err); 22 | else this.resolve(res); 23 | } 24 | 25 | cl.getAsync = function(id) { 26 | var def = q.defer() 27 | cl.get(id, bind(nodeback, def)); 28 | return def.promise; 29 | } 30 | 31 | module.exports = function upload(stream, idOrPath, tag, done) { 32 | cl.getAsync('bench-'+(stream & 511)) 33 | .then(function(ok) { done(null, ok) }, done); 34 | } 35 | 36 | module.exports.end = function() { 37 | cl.end(); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /examples-redis/promises-q.js: -------------------------------------------------------------------------------- 1 | global.useQ = true; 2 | 3 | var q = require('q'); 4 | 5 | var redis = require('redis'); 6 | 7 | var cl = redis.createClient(); 8 | 9 | /* 10 | for (var k = 0; k < 512; ++k) { 11 | cl.set('bench-'+k, 'some value contained'); 12 | } 13 | */ 14 | 15 | cl.getAsync = q.nbind(cl.get, cl); 16 | 17 | 18 | 19 | module.exports = function upload(stream, idOrPath, tag, done) { 20 | cl.getAsync('bench-'+(stream & 511)).nodeify(done); 21 | } 22 | 23 | module.exports.end = function() { 24 | cl.end(); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /examples/callbacks-async-waterfall.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | var async = require('async'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobId, file, version, fileId; 8 | async.waterfall([ 9 | function writeBlob(callback) { 10 | blob.put(stream, callback); 11 | }, 12 | function afterBlobWritten(callback) { 13 | blobId = undefined // iBlobId; 14 | self.byUuidOrPath(idOrPath).get(callback); 15 | }, 16 | function afterFileFetched(callback) { 17 | file = undefined; //iFile; 18 | var previousId = file ? file.version : null; 19 | version = { 20 | userAccountId: userAccount.id, 21 | date: new Date(), 22 | blobId: blobId, 23 | creatorId: userAccount.id, 24 | previousId: previousId, 25 | mergedId: null, 26 | mergeType: 'mine', 27 | comment: '', 28 | tag: tag 29 | }; 30 | version.id = Version.createHash(version); 31 | Version.insert(version).execWithin(tx, callback); 32 | }, 33 | function afterVersionInserted(callback) { 34 | if (!file) { 35 | var splitPath = idOrPath.split('/'); 36 | var fileName = splitPath[splitPath.length - 1]; 37 | var newId = uuid.v1(); 38 | self.createQuery(idOrPath, { 39 | id: newId, 40 | userAccountId: userAccount.id, 41 | type: 'file', 42 | name: fileName, 43 | version: version.id 44 | }, function (err, q) { 45 | if (err) return backoff(err); 46 | q.execWithin(tx, function (err) { 47 | callback(err, newId); 48 | }); 49 | 50 | }) 51 | } 52 | else return callback(null, file.id); 53 | }, 54 | function afterFileExists(iFileId, callback) { 55 | fileId = iFileId; 56 | FileVersion.insert({fileId: fileId, versionId: version.id}) 57 | .execWithin(tx, callback); 58 | }, 59 | function afterFileVersionInserted(callback) { 60 | File.whereUpdate({id: fileId}, { version: version.id }) 61 | .execWithin(tx, callback); 62 | }, 63 | function afterFileUpdated(callback) { 64 | tx.commit(callback); 65 | } 66 | ], 67 | function (err) { 68 | if (err) tx.rollback(); 69 | done(err); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /examples/callbacks-catcher.js: -------------------------------------------------------------------------------- 1 | var catcher = require('../lib/catcher'); 2 | require('../lib/fakes'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var c = catcher(); 8 | blob.put(stream, c.try(function (blobId) { 9 | self.byUuidOrPath(idOrPath).get(c.try(function (file) { 10 | var previousId = file ? file.version : null; 11 | var version = { 12 | userAccountId: userAccount.id, 13 | date: new Date(), 14 | blobId: blobId, 15 | creatorId: userAccount.id, 16 | previousId: previousId, 17 | }; 18 | version.id = Version.createHash(version); 19 | Version.insert(version).execWithin(tx, c.try(function () { 20 | if (!file) { 21 | var splitPath = idOrPath.split('/'); 22 | var fileName = splitPath[splitPath.length - 1]; 23 | var newId = uuid.v1(); 24 | self.createQuery(idOrPath, { 25 | id: newId, 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | }, c.try(function (q) { 30 | q.execWithin(tx, c.try(function () { 31 | afterFileExists(newId); 32 | })); 33 | })) 34 | } 35 | else return afterFileExists(file.id); 36 | })); 37 | function afterFileExists(fileId) { 38 | FileVersion.insert({fileId: fileId,versionId: version.id}) 39 | .execWithin(tx, c.try(function () { 40 | File.whereUpdate({id: fileId}, { 41 | version: version.id 42 | }).execWithin(tx, c.try(function () { 43 | tx.commit(done); 44 | })); 45 | })); 46 | } 47 | })); 48 | })); 49 | c.catch(function backoff(err) { 50 | tx.rollback(); 51 | return done(err); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /examples/callbacks-deferred-queue.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | var dq = require('deferred-queue'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobId, file, version, fileId; 8 | dq () 9 | .on ("error", function (error){console.log(error) 10 | tx.rollback(); 11 | }) 12 | .push (function writeBlob(callback) { 13 | blob.put(stream, callback); 14 | }) 15 | .push (function afterBlobWritten(callback) { 16 | blobId = undefined // iBlobId; 17 | self.byUuidOrPath(idOrPath).get(callback); 18 | }) 19 | .push (function afterFileFetched(callback) { 20 | file = undefined; //iFile; 21 | var previousId = file ? file.version : null; 22 | version = { 23 | userAccountId: userAccount.id, 24 | date: new Date(), 25 | blobId: blobId, 26 | creatorId: userAccount.id, 27 | previousId: previousId, 28 | mergedId: null, 29 | mergeType: 'mine', 30 | comment: '', 31 | tag: tag 32 | }; 33 | version.id = Version.createHash(version); 34 | Version.insert(version).execWithin(tx, callback); 35 | }) 36 | .push (function afterVersionInserted(callback) { 37 | if (!file) { 38 | var splitPath = idOrPath.split('/'); 39 | var fileName = splitPath[splitPath.length - 1]; 40 | var newId = uuid.v1(); 41 | self.createQuery(idOrPath, { 42 | id: newId, 43 | userAccountId: userAccount.id, 44 | type: 'file', 45 | name: fileName, 46 | version: version.id 47 | }, function (err, q) { 48 | if (err) return backoff(err); 49 | q.execWithin(tx, function (err) { 50 | callback(err, newId); 51 | }); 52 | 53 | }) 54 | } 55 | else return callback(null, file.id); 56 | }, function (error, id){ 57 | fileId = id; 58 | }) 59 | .push (function afterFileExists(callback) { 60 | FileVersion.insert({fileId: fileId, versionId: version.id}) 61 | .execWithin(tx, callback); 62 | }) 63 | .push (function afterFileVersionInserted(callback) { 64 | File.whereUpdate({id: fileId}, { version: version.id }) 65 | .execWithin(tx, callback); 66 | }) 67 | .push (function afterFileUpdated(callback) { 68 | tx.commit(callback); 69 | }) 70 | .push (done); 71 | } 72 | -------------------------------------------------------------------------------- /examples/callbacks-flattened-class-ctx.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes-ctx'); 2 | 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | new Uploader(stream, idOrPath, tag, done).run(); 6 | } 7 | 8 | function Uploader(stream, idOrPath, tag, done) { 9 | this.stream = stream; 10 | this.idOrPath = idOrPath; 11 | this.tag = tag; 12 | this.done = done; 13 | this.file = null; 14 | this.version = null; 15 | this.blobId = null; 16 | } 17 | 18 | Uploader.prototype.backoff = function backoff (err) { 19 | this.tx.rollback(); 20 | return this.done(err); 21 | } 22 | 23 | 24 | Uploader.prototype.run = function run () { 25 | var blob = blobManager.create(account); 26 | this.tx = db.begin(); 27 | blob.put(this.stream, this.afterBlobWritten, this); 28 | } 29 | 30 | Uploader.prototype.afterBlobWritten = function afterBlobWritten(err, blobId) { 31 | if (err) return this.done(err); 32 | this.blobId = blobId; 33 | self.byUuidOrPath(this.idOrPath) 34 | .get(this.afterFileFetched, this); 35 | } 36 | 37 | Uploader.prototype.afterFileFetched = function afterFileFetched(err, file) { 38 | if (err) return this.done(err); 39 | this.file = file; 40 | 41 | var previousId = file ? file.version : null; 42 | var version = this.version = { 43 | userAccountId: userAccount.id, 44 | date: new Date(), 45 | blobId: this.blobId, 46 | creatorId: userAccount.id, 47 | previousId: previousId, 48 | mergedId: null, 49 | mergeType: 'mine', 50 | comment: '', 51 | tag: this.tag 52 | }; 53 | version.id = Version.createHash(version); 54 | Version.insert(version).execWithin( 55 | this.tx, this.afterVersionInserted, this); 56 | } 57 | 58 | Uploader.prototype.afterVersionInserted = function afterVersionInserted(err) { 59 | if (err) return this.backoff(err); 60 | if (!this.file) { 61 | var splitPath = this.idOrPath.split('/'); 62 | var fileName = splitPath[splitPath.length - 1]; 63 | var file = this.file = { 64 | id: uuid.v1(), 65 | userAccountId: userAccount.id, 66 | type: 'file', 67 | name: fileName, 68 | version: this.version.id 69 | }; 70 | self.createQuery(this.idOrPath, file, 71 | this.afterQueryCreated, this); 72 | } 73 | else return afterFileExists.call(this); 74 | } 75 | 76 | Uploader.prototype.afterQueryCreated = function afterQueryCreated(err, q) { 77 | if (err) return this.backoff(err); 78 | q.execWithin(this.tx, this.afterFileExists, this); 79 | } 80 | 81 | Uploader.prototype.afterFileExists = function afterFileExists(err) { 82 | 83 | if (err) return this.backoff(err); 84 | 85 | FileVersion.insert({fileId: this.file.id, versionId: this.version.id}) 86 | .execWithin(this.tx, this.afterFileVersionInserted, this); 87 | } 88 | 89 | Uploader.prototype.afterFileVersionInserted = function afterFileVersionInserted(err) { 90 | if (err) return this.backoff(err); 91 | File.whereUpdate({id: this.file.id}, { version: this.version.id }) 92 | .execWithin(this.tx, this.afterFileUpdated, this); 93 | } 94 | 95 | Uploader.prototype.afterFileUpdated = function afterFileUpdated(err) { 96 | if (err) return this.backoff(err); 97 | this.tx.commit(this.done); 98 | } 99 | 100 | 101 | -------------------------------------------------------------------------------- /examples/callbacks-flattened-class.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | function bind(f, ctx) { 3 | return function bound() { 4 | return f.apply(ctx, arguments); 5 | } 6 | } 7 | 8 | module.exports = function upload(stream, idOrPath, tag, done) { 9 | new Uploader(stream, idOrPath, tag, done).run(); 10 | } 11 | 12 | function Uploader(stream, idOrPath, tag, done) { 13 | this.stream = stream; 14 | this.idOrPath = idOrPath; 15 | this.tag = tag; 16 | this.done = done; 17 | this.file = null; 18 | this.version = null; 19 | this.blobId = null; 20 | } 21 | 22 | Uploader.prototype.backoff = function backoff (err) { 23 | this.tx.rollback(); 24 | return this.done(err); 25 | } 26 | 27 | 28 | Uploader.prototype.run = function run () { 29 | var blob = blobManager.create(account); 30 | this.tx = db.begin(); 31 | blob.put(this.stream, bind(this.afterBlobWritten, this)); 32 | } 33 | 34 | Uploader.prototype.afterBlobWritten = function afterBlobWritten(err, blobId) { 35 | if (err) return this.done(err); 36 | this.blobId = blobId; 37 | self.byUuidOrPath(this.idOrPath) 38 | .get(bind(this.afterFileFetched, this)); 39 | } 40 | 41 | Uploader.prototype.afterFileFetched = function afterFileFetched(err, file) { 42 | if (err) return this.done(err); 43 | this.file = file; 44 | 45 | var previousId = file ? file.version : null; 46 | var version = this.version = { 47 | userAccountId: userAccount.id, 48 | date: new Date(), 49 | blobId: this.blobId, 50 | creatorId: userAccount.id, 51 | previousId: previousId, 52 | mergedId: null, 53 | mergeType: 'mine', 54 | comment: '', 55 | tag: this.tag 56 | }; 57 | version.id = Version.createHash(version); 58 | Version.insert(version).execWithin( 59 | this.tx, bind(this.afterVersionInserted, this)); 60 | } 61 | 62 | Uploader.prototype.afterVersionInserted = function afterVersionInserted(err) { 63 | if (err) return this.backoff(err); 64 | if (!this.file) { 65 | var splitPath = this.idOrPath.split('/'); 66 | var fileName = splitPath[splitPath.length - 1]; 67 | var file = this.file = { 68 | id: uuid.v1(), 69 | userAccountId: userAccount.id, 70 | type: 'file', 71 | name: fileName, 72 | version: this.version.id 73 | }; 74 | self.createQuery(this.idOrPath, file, 75 | bind(this.afterQueryCreated, this)); 76 | } 77 | else return afterFileExists(); 78 | } 79 | 80 | Uploader.prototype.afterQueryCreated = function afterQueryCreated(err, q) { 81 | if (err) return this.backoff(err); 82 | q.execWithin(this.tx, bind(this.afterFileExists, this)); 83 | } 84 | 85 | Uploader.prototype.afterFileExists = function afterFileExists(err) { 86 | 87 | if (err) return this.backoff(err); 88 | 89 | FileVersion.insert({fileId: this.file.id, versionId: this.version.id}) 90 | .execWithin(this.tx, bind(this.afterFileVersionInserted, this)); 91 | } 92 | 93 | Uploader.prototype.afterFileVersionInserted = function afterFileVersionInserted(err) { 94 | if (err) return this.backoff(err); 95 | File.whereUpdate({id: this.file.id}, { version: this.version.id }) 96 | .execWithin(this.tx, bind(this.afterFileUpdated, this)); 97 | } 98 | 99 | Uploader.prototype.afterFileUpdated = function afterFileUpdated(err) { 100 | if (err) return this.backoff(err); 101 | this.tx.commit(this.done); 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /examples/callbacks-flattened-passing.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, done) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | blob.put(stream, afterBlobWritten(tx, idOrPath, tag, done)); 7 | var blobId; 8 | } 9 | 10 | function backoff(err, tx, done) { 11 | tx.rollback(); 12 | return done(err); 13 | } 14 | function afterBlobWritten(tx, idOrPath, tag, done) { 15 | return function (err, iBlobId) { 16 | if (err) return done(err); 17 | blobId = iBlobId; 18 | self.byUuidOrPath(idOrPath).get(afterFileFetched(tx, idOrPath, tag, done)); 19 | } 20 | } 21 | 22 | function afterFileFetched(tx, idOrPath, tag, done) { 23 | return function(err, file) { 24 | if (err) return done(err); 25 | var previousId = file ? file.version : null; 26 | version = { 27 | userAccountId: userAccount.id, 28 | date: new Date(), 29 | blobId: blobId, 30 | creatorId: userAccount.id, 31 | previousId: previousId, 32 | mergedId: null, 33 | mergeType: 'mine', 34 | comment: '', 35 | tag: tag 36 | }; 37 | version.id = Version.createHash(version); 38 | Version.insert(version).execWithin( 39 | tx, afterVersionInserted(tx, file, version, idOrPath, done)); 40 | } 41 | } 42 | function afterVersionInserted(tx, file, version, idOrPath, done) { 43 | return function (err) { 44 | if (err) return backoff(err, tx, done); 45 | if (!file) { 46 | var splitPath = idOrPath.split('/'); 47 | var fileName = splitPath[splitPath.length - 1]; 48 | var file = { 49 | id: uuid.v1(), 50 | userAccountId: userAccount.id, 51 | type: 'file', 52 | name: fileName, 53 | version: version.id 54 | }; 55 | self.createQuery(idOrPath, file, function (err, q) { 56 | if (err) return backoff(err, tx, done); 57 | q.execWithin(tx, function (err) { 58 | afterFileExists(err, tx, file, version, done); 59 | }); 60 | 61 | }) 62 | } 63 | else return afterFileExists(null, tx, file, version, done); 64 | } 65 | } 66 | 67 | function afterFileExists(err, tx, file, version, done) { 68 | 69 | if (err) return backoff(err, tx, done); 70 | FileVersion.insert({fileId: file.id, versionId: version.id}) 71 | .execWithin(tx, afterFileVersionInserted(tx, file, version, done)); 72 | } 73 | 74 | function afterFileVersionInserted(tx, file, version, done) { 75 | return function (err) { 76 | if (err) return backoff(err, tx, done); 77 | File.whereUpdate({id: file.id}, { version: version.id }) 78 | .execWithin(tx, afterFileUpdated(tx, done)); 79 | } 80 | } 81 | function afterFileUpdated(tx, done) { 82 | return function(err) { 83 | if (err) return backoff(err, tx, done); 84 | tx.commit(done); 85 | } 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples/callbacks-flattened.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, done) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | function backoff(err) { 7 | tx.rollback(); 8 | return done(err); 9 | } 10 | blob.put(stream, afterBlobWritten); 11 | var blobId; 12 | function afterBlobWritten(err, iBlobId) { 13 | if (err) return done(err); 14 | blobId = iBlobId; 15 | self.byUuidOrPath(idOrPath).get(afterFileFetched); 16 | } 17 | var file, version; 18 | function afterFileFetched(err, iFile) { 19 | if (err) return done(err); 20 | file = iFile; 21 | var previousId = file ? file.version : null; 22 | version = { 23 | userAccountId: userAccount.id, 24 | date: new Date(), 25 | blobId: blobId, 26 | creatorId: userAccount.id, 27 | previousId: previousId, 28 | mergedId: null, 29 | mergeType: 'mine', 30 | comment: '', 31 | tag: tag 32 | }; 33 | version.id = Version.createHash(version); 34 | Version.insert(version).execWithin(tx, afterVersionInserted); 35 | } 36 | function afterVersionInserted(err) { 37 | if (err) return backoff(err); 38 | if (!file) { 39 | var splitPath = idOrPath.split('/'); 40 | var fileName = splitPath[splitPath.length - 1]; 41 | var newId = uuid.v1(); 42 | self.createQuery(idOrPath, { 43 | id: newId, 44 | userAccountId: userAccount.id, 45 | type: 'file', 46 | name: fileName, 47 | version: version.id 48 | }, function (err, q) { 49 | if (err) return backoff(err); 50 | q.execWithin(tx, function (err) { 51 | afterFileExists(err, newId); 52 | }); 53 | 54 | }) 55 | } 56 | else return afterFileExists(null, file.id); 57 | } 58 | var fileId; 59 | function afterFileExists(err, iFileId) { 60 | fileId = iFileId; 61 | if (err) return backoff(err); 62 | FileVersion.insert({fileId: fileId, versionId: version.id}) 63 | .execWithin(tx, afterFileVersionInserted); 64 | } 65 | function afterFileVersionInserted(err) { 66 | if (err) return backoff(err); 67 | File.whereUpdate({id: fileId}, { version: version.id }) 68 | .execWithin(tx, afterFileUpdated); 69 | } 70 | function afterFileUpdated(err) { 71 | if (err) return backoff(err); 72 | tx.commit(done); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/callbacks-generator-genny.js: -------------------------------------------------------------------------------- 1 | var genny = require('genny'); 2 | require('../lib/fakes'); 3 | 4 | module.exports = genny.fn(function* upload(stream, idOrPath, tag, resume) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | try { 8 | var blobId = yield blob.put(stream, resume()); 9 | var file = yield self.byUuidOrPath(idOrPath).get(resume()); 10 | var previousId = file ? file.version : null; 11 | var version = { 12 | userAccountId: userAccount.id, 13 | date: new Date(), 14 | blobId: blobId, 15 | creatorId: userAccount.id, 16 | previousId: previousId, 17 | }; 18 | version.id = Version.createHash(version); 19 | yield Version.insert(version).execWithin(tx, resume()); 20 | if (!file) { 21 | var splitPath = idOrPath.split('/'); 22 | var fileName = splitPath[splitPath.length - 1]; 23 | var newId = uuid.v1(); 24 | var file = { 25 | id: newId, 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | } 30 | var q = yield self.createQuery(idOrPath, file, resume()); 31 | yield q.execWithin(tx, resume()); 32 | } 33 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 34 | .execWithin(tx, resume()); 35 | yield File.whereUpdate({id: file.id}, {version: version.id}) 36 | .execWithin(tx, resume()); 37 | yield tx.commit(resume()); 38 | } catch (err) { 39 | tx.rollback(); 40 | throw err; 41 | } 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /examples/callbacks-generator-suspend.js: -------------------------------------------------------------------------------- 1 | var suspend= require('suspend'); 2 | require('../lib/fakes'); 3 | 4 | module.exports = suspend(function* upload(stream, idOrPath, tag, done, resume) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | try { 8 | var blobId = yield blob.put(stream, resume); 9 | var file = yield self.byUuidOrPath(idOrPath).get(resume); 10 | var previousId = file ? file.version : null; 11 | var version = { 12 | userAccountId: userAccount.id, 13 | date: new Date(), 14 | blobId: blobId, 15 | creatorId: userAccount.id, 16 | previousId: previousId, 17 | }; 18 | version.id = Version.createHash(version); 19 | yield Version.insert(version).execWithin(tx, resume); 20 | if (!file) { 21 | var splitPath = idOrPath.split('/'); 22 | var fileName = splitPath[splitPath.length - 1]; 23 | var newId = uuid.v1(); 24 | var file = { 25 | id: newId, 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | } 30 | var q = yield self.createQuery(idOrPath, file, resume); 31 | yield q.execWithin(tx, resume); 32 | } 33 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 34 | .execWithin(tx, resume); 35 | yield File.whereUpdate({id: file.id}, {version: version.id}) 36 | .execWithin(tx, resume); 37 | yield tx.commit(resume); 38 | return done(); 39 | } catch (e) { 40 | tx.rollback(); 41 | return done(e); 42 | } 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /examples/callbacks-original.js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, done) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | function backoff(err) { 7 | tx.rollback(); 8 | return done(err); 9 | } 10 | blob.put(stream, function (err, blobId) { 11 | if (err) return done(err); 12 | self.byUuidOrPath(idOrPath).get(function (err, file) { 13 | if (err) return done(err); 14 | var previousId = file ? file.version : null; 15 | var version = { 16 | userAccountId: userAccount.id, 17 | date: new Date(), 18 | blobId: blobId, 19 | creatorId: userAccount.id, 20 | previousId: previousId, 21 | }; 22 | version.id = Version.createHash(version); 23 | Version.insert(version).execWithin(tx, function (err) { 24 | if (err) return backoff(err); 25 | if (!file) { 26 | var splitPath = idOrPath.split('/'); 27 | var fileName = splitPath[splitPath.length - 1]; 28 | var newId = uuid.v1(); 29 | self.createQuery(idOrPath, { 30 | id: newId, 31 | userAccountId: userAccount.id, 32 | name: fileName, 33 | version: version.id 34 | }, function (err, q) { 35 | if (err) return backoff(err); 36 | q.execWithin(tx, function (err) { 37 | afterFileExists(err, newId); 38 | }); 39 | 40 | }) 41 | } 42 | else return afterFileExists(null, file.id); 43 | }); 44 | function afterFileExists(err, fileId) { 45 | if (err) return backoff(err); 46 | FileVersion.insert({fileId: fileId,versionId: version.id}) 47 | .execWithin(tx, function (err) { 48 | if (err) return backoff(err); 49 | File.whereUpdate({id: fileId}, { 50 | version: version.id 51 | }).execWithin(tx, function (err) { 52 | if (err) return backoff(err); 53 | tx.commit(done); 54 | }); 55 | }) 56 | } 57 | }); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /examples/dst-callbacks-generator-genny-traceur.js: -------------------------------------------------------------------------------- 1 | require('traceur').require.makeDefault(); 2 | require('source-map-support').install(); 3 | module.exports = require('./callbacks-generator-genny.js'); 4 | -------------------------------------------------------------------------------- /examples/dst-callbacks-generator-suspend-traceur.js: -------------------------------------------------------------------------------- 1 | require('traceur').require.makeDefault(); 2 | require('source-map-support').install(); 3 | module.exports = require('./callbacks-generator-suspend.js'); 4 | -------------------------------------------------------------------------------- /examples/dst-promises-q-generator-traceur.js: -------------------------------------------------------------------------------- 1 | require('traceur').require.makeDefault(); 2 | require('source-map-support').install(); 3 | module.exports = require('./promises-q-generator.js'); 4 | -------------------------------------------------------------------------------- /examples/dst-stratifiedjs-compiled.js: -------------------------------------------------------------------------------- 1 | require('stratifiedjs'); 2 | 3 | __oni_rt.exseq(this.arguments,this,'examples/src-stratifiedjs.sjs',[24,__oni_rt.C(function(){return require('../lib/fakesSJS-dst.js')},1),__oni_rt.Nb(function(){return module.exports=function upload(stream,idOrPath,tag,done){var blob,blobId,file,previousId,version,tx,fileId,splitPath,fileName;return __oni_rt.exseq(arguments,this,'examples/src-stratifiedjs.sjs',[1,__oni_rt.Nb(function(){blob=blobManager.create(account);},5),__oni_rt.Sc(6,function(_oniX){return blobId=_oniX;},__oni_rt.C(function(){return blob.put(stream)},5)),__oni_rt.Sc(7,function(_oniX){return file=_oniX;},__oni_rt.Fcall(1,6,__oni_rt.Sc(6,function(l){return [l,'get'];},__oni_rt.C(function(){return self.byUuidOrPath(idOrPath)},6)))),__oni_rt.Nb(function(){previousId=file?file.version:null;version={userAccountId:userAccount.id,date:new Date(),blobId:blobId,creatorId:userAccount.id,previousId:previousId};version.id=Version.createHash(version);tx=db.begin();},0),__oni_rt.Try(0,__oni_rt.Seq(0,__oni_rt.Fcall(1,19,__oni_rt.Sc(19,function(l){return [l,'execWithin'];},__oni_rt.C(function(){return Version.insert(version)},19)),__oni_rt.Nb(function(){return tx},19)),__oni_rt.Nb(function(){if(! file)return __oni_rt.ex(__oni_rt.Nb(function(){splitPath=idOrPath.split('/');fileName=splitPath[splitPath.length - 1];fileId=uuid.v1();return self.createQuery(idOrPath,{id:fileId,userAccountId:userAccount.id,name:fileName,version:version.id}).execWithin(tx);},0),this);else return __oni_rt.ex(__oni_rt.Nb(function(){return fileId=file.id},36),this);},22),__oni_rt.Fcall(1,39,__oni_rt.Sc(39,function(l){return [l,'execWithin'];},__oni_rt.C(function(){return FileVersion.insert({fileId:fileId,versionId:version.id})},39)),__oni_rt.Nb(function(){return tx},39)),__oni_rt.Fcall(1,41,__oni_rt.Sc(41,function(l){return [l,'execWithin'];},__oni_rt.C(function(){return File.whereUpdate({id:fileId},{version:version.id})},41)),__oni_rt.Nb(function(){return tx},41))),function(__oni_env,e){return __oni_rt.ex(__oni_rt.Seq(0,__oni_rt.C(function(){return tx.rollback()},43),__oni_rt.Nb(function(){return done(e);},44)),__oni_env)},0),__oni_rt.C(function(){return tx.commit()},46),__oni_rt.Nb(function(){return done();},47)])};},49)]) 4 | 5 | -------------------------------------------------------------------------------- /examples/dst-streamline-fibers.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.6.0 (fibers) - DO NOT EDIT ***/var fstreamline__ = require("streamline/lib/fibers/runtime"); ((function(fstreamline_F__) { function F(_) { return fstreamline_F__.apply(this, arguments); }; F.fstreamlineFunction = fstreamline_F__.fstreamlineFunction; return F;})(fstreamline__.create(function(_) { require('../lib/fakes'); 2 | 3 | module.exports = (function(fstreamline_F__) { function F(stream, idOrPath, tag, _) { return fstreamline_F__.apply(this, arguments); }; F.fstreamlineFunction = fstreamline_F__.fstreamlineFunction; return F;})(fstreamline__.create(function upload(stream, idOrPath, tag, _) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | try { 7 | var blobId = fstreamline__.invoke(blob, "put", [stream, _], 1); 8 | var file = fstreamline__.invoke(self.byUuidOrPath(idOrPath), "get", [_], 0); 9 | var previousId = file ? file.version : null; 10 | var version = { 11 | userAccountId: userAccount.id, 12 | date: new Date(), 13 | blobId: blobId, 14 | creatorId: userAccount.id, 15 | previousId: previousId, 16 | }; 17 | version.id = Version.createHash(version); 18 | fstreamline__.invoke(Version.insert(version), "execWithin", [tx, _], 1); 19 | if (!file) { 20 | var splitPath = idOrPath.split('/'); 21 | var fileName = splitPath[splitPath.length - 1]; 22 | var newId = uuid.v1(); 23 | var file = { 24 | id: newId, 25 | userAccountId: userAccount.id, 26 | name: fileName, 27 | version: version.id 28 | }; 29 | var q = fstreamline__.invoke(self, "createQuery", [idOrPath, file, _], 2); 30 | fstreamline__.invoke(q, "execWithin", [tx, _], 1); 31 | } 32 | fstreamline__.invoke(FileVersion.insert({fileId: file.id,versionId: version.id}), "execWithin", [tx, _], 1); 33 | fstreamline__.invoke(File.whereUpdate({id: file.id}, {version: version.id}), "execWithin", [tx, _], 1); 34 | fstreamline__.invoke(tx, "commit", [_], 0); 35 | } catch (err) { 36 | tx.rollback(); 37 | throw err; //Error(err); 38 | } 39 | }, 3)); 40 | 41 | 42 | }, 0)).call(this, function(err) { 43 | if (err) throw err; 44 | })); -------------------------------------------------------------------------------- /examples/dst-streamline.js: -------------------------------------------------------------------------------- 1 | /*** Generated by streamline 0.6.0 (callbacks) - DO NOT EDIT ***/ var __rt=require('streamline/lib/callbacks/runtime').runtime(__filename),__func=__rt.__func,__cb=__rt.__cb,__tryCatch=__rt.__tryCatch; require("../lib/fakes"); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, _) { var blob, tx, blobId, file, previousId, version, splitPath, fileName, newId, q; var __frame = { name: "upload", line: 3 }; return __func(_, this, arguments, upload, 3, __frame, function __$upload() { 4 | blob = blobManager.create(account); 5 | tx = db.begin(); return (function ___(__then) { (function ___(_) { __tryCatch(_, function __$upload() { 6 | 7 | return blob.put(stream, __cb(_, __frame, 4, 22, function ___(__0, __1) { blobId = __1; 8 | return self.byUuidOrPath(idOrPath).get(__cb(_, __frame, 5, 20, function ___(__0, __2) { file = __2; 9 | previousId = (file ? file.version : null); 10 | version = { 11 | userAccountId: userAccount.id, 12 | date: new Date(), 13 | blobId: blobId, 14 | creatorId: userAccount.id, 15 | previousId: previousId }; 16 | 17 | version.id = Version.createHash(version); 18 | return Version.insert(version).execWithin(tx, __cb(_, __frame, 15, 8, function __$upload() { return (function __$upload(__then) { 19 | if (!file) { 20 | splitPath = idOrPath.split("/"); 21 | fileName = splitPath[(splitPath.length - 1)]; 22 | newId = uuid.v1(); 23 | file = { 24 | id: newId, 25 | userAccountId: userAccount.id, 26 | name: fileName, 27 | version: version.id }; 28 | 29 | return self.createQuery(idOrPath, file, __cb(_, __frame, 26, 21, function ___(__0, __3) { q = __3; 30 | return q.execWithin(tx, __cb(_, __frame, 27, 13, __then, true)); }, true)); } else { __then(); } ; })(function __$upload() { 31 | 32 | 33 | return FileVersion.insert({ fileId: file.id, versionId: version.id }).execWithin(tx, __cb(_, __frame, 30, 8, function __$upload() { 34 | 35 | return File.whereUpdate({ id: file.id }, { version: version.id }).execWithin(tx, __cb(_, __frame, 32, 8, function __$upload() { 36 | return tx.commit(__cb(_, __frame, 33, 8, __then, true)); }, true)); }, true)); }); }, true)); }, true)); }, true)); }); })(function ___(err, __result) { __tryCatch(_, function __$upload() { if (err) { 37 | 38 | tx.rollback(); 39 | return _(err); } else { _(null, __result); } ; }); }); })(function ___() { __tryCatch(_, _); }); });}; -------------------------------------------------------------------------------- /examples/dst-thunks-generator-co-traceur.js: -------------------------------------------------------------------------------- 1 | require('traceur').require.makeDefault(); 2 | require('source-map-support').install(); 3 | module.exports = require('./thunks-generator-co.js'); 4 | -------------------------------------------------------------------------------- /examples/fibrous.js: -------------------------------------------------------------------------------- 1 | var fibrous = require('fibrous'); 2 | require('../lib/fakes'); 3 | 4 | module.exports = fibrous(function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | try { 8 | var blobId = blob.sync.put(stream); 9 | var file = self.byUuidOrPath(idOrPath).sync.get(); 10 | var previousId = file ? file.version : null; 11 | var version = { 12 | userAccountId: userAccount.id, 13 | date: new Date(), 14 | blobId: blobId, 15 | creatorId: userAccount.id, 16 | previousId: previousId, 17 | }; 18 | version.id = Version.createHash(version); 19 | Version.insert(version).sync.execWithin(tx); 20 | if (!file) { 21 | var splitPath = idOrPath.split('/'); 22 | var fileName = splitPath[splitPath.length - 1]; 23 | var newId = uuid.v1(); 24 | var file = { 25 | id: newId, 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | } 30 | var q = self.sync.createQuery(idOrPath, file); 31 | q.sync.execWithin(tx); 32 | } 33 | FileVersion.insert({fileId: file.id, versionId: version.id}) 34 | .sync.execWithin(tx); 35 | File.whereUpdate({id: file.id}, {version: version.id}) 36 | .sync.execWithin(tx); 37 | tx.sync.commit(); 38 | } catch (err) { 39 | tx.sync.rollback(); 40 | throw err; 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /examples/promises-bluebird-ctx.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); 2 | require('../lib/fakesP-ctx'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobIdP = blob.put(stream); 8 | var fileP = self.byUuidOrPath(idOrPath).get(); 9 | var version, fileId, file; 10 | 11 | bluebird.all([blobIdP, fileP]).spread(function(blobId, fileV) { 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQuery(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-bluebird-generator-ctx.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); 2 | require('../lib/fakesP-ctx'); 3 | 4 | module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { 5 | try { 6 | var blob = blobManager.create(account); 7 | var tx = db.begin(); 8 | //console.log("1"); 9 | var blobId = yield blob.put(stream); 10 | //console.log("2"); 11 | var file = yield self.byUuidOrPath(idOrPath).get(); 12 | //console.log("3"); 13 | 14 | var previousId = file ? file.version : null; 15 | version = { 16 | userAccountId: userAccount.id, 17 | date: new Date(), 18 | blobId: blobId, 19 | creatorId: userAccount.id, 20 | previousId: previousId, 21 | }; 22 | version.id = Version.createHash(version); 23 | yield Version.insert(version).execWithin(tx); 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | file = { 28 | id: uuid.v1(), 29 | userAccountId: userAccount.id, 30 | name: fileName, 31 | version: version.id 32 | } 33 | var query = yield self.createQuery(idOrPath, file); 34 | yield query.execWithin(tx); 35 | } 36 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 37 | .execWithin(tx); 38 | yield File.whereUpdate({id: file.id}, {version: version.id}) 39 | .execWithin(tx); 40 | tx.commit(); 41 | done(); 42 | } catch (err) { 43 | tx.rollback(); 44 | done(err); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /examples/promises-bluebird-generator.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); 2 | require('../lib/fakesP')(bluebird.promisify); 3 | 4 | module.exports = bluebird.coroutine(function* upload(stream, idOrPath, tag, done) { 5 | try { 6 | var blob = blobManager.create(account); 7 | var tx = db.begin(); 8 | var blobId = yield blob.put(stream); 9 | var file = yield self.byUuidOrPath(idOrPath).get(); 10 | 11 | var previousId = file ? file.version : null; 12 | version = { 13 | userAccountId: userAccount.id, 14 | date: new Date(), 15 | blobId: blobId, 16 | creatorId: userAccount.id, 17 | previousId: previousId, 18 | }; 19 | version.id = Version.createHash(version); 20 | yield Version.insert(version).execWithin(tx); 21 | if (!file) { 22 | var splitPath = idOrPath.split('/'); 23 | var fileName = splitPath[splitPath.length - 1]; 24 | file = { 25 | id: uuid.v1(), 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | } 30 | var query = yield self.createQuery(idOrPath, file); 31 | yield query.execWithin(tx); 32 | } 33 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 34 | .execWithin(tx); 35 | yield File.whereUpdate({id: file.id}, {version: version.id}) 36 | .execWithin(tx); 37 | tx.commit(); 38 | done(); 39 | } catch (err) { 40 | tx.rollback(); 41 | done(err); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /examples/promises-bluebird-spawn.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); 2 | require('../lib/fakesP')(bluebird.promisify); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | bluebird.spawn(function* () { 6 | try { 7 | var blob = blobManager.create(account); 8 | var tx = db.begin(); 9 | var blobId = yield blob.put(stream); 10 | var file = yield self.byUuidOrPath(idOrPath).get(); 11 | 12 | var previousId = file ? file.version : null; 13 | version = { 14 | userAccountId: userAccount.id, 15 | date: new Date(), 16 | blobId: blobId, 17 | creatorId: userAccount.id, 18 | previousId: previousId, 19 | }; 20 | version.id = Version.createHash(version); 21 | yield Version.insert(version).execWithin(tx); 22 | if (!file) { 23 | var splitPath = idOrPath.split('/'); 24 | var fileName = splitPath[splitPath.length - 1]; 25 | file = { 26 | id: uuid.v1(), 27 | userAccountId: userAccount.id, 28 | name: fileName, 29 | version: version.id 30 | } 31 | var query = yield self.createQuery(idOrPath, file); 32 | yield query.execWithin(tx); 33 | } 34 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 35 | .execWithin(tx); 36 | yield File.whereUpdate({id: file.id}, {version: version.id}) 37 | .execWithin(tx); 38 | tx.commit(); 39 | } catch (err) { 40 | tx.rollback(); 41 | throw err; 42 | } 43 | }).done(done, done); 44 | } 45 | -------------------------------------------------------------------------------- /examples/promises-bluebird.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); 2 | require('../lib/fakesP')(bluebird.promisify); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobIdP = blob.put(stream); 8 | var fileP = self.byUuidOrPath(idOrPath).get(); 9 | var version, fileId, file; 10 | 11 | bluebird.all([blobIdP, fileP]).spread(function(blobId, fileV) { 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQuery(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-compose-bluebird.js: -------------------------------------------------------------------------------- 1 | var q = require('bluebird'), 2 | fn = require('when/function'); 3 | 4 | require('../lib/fakesP')(q.promisify); 5 | 6 | // Assume Version = objectLift(version), File = objectLift(file) etc. 7 | module.exports = function upload(stream, idOrPath, tag, done) { 8 | var blob = blobManager.create(account); 9 | var tx = db.begin(); 10 | var blobIdP = blob.put(stream); 11 | var fileP = self.byUuidOrPath(idOrPath).get(); 12 | var previousIdP = fileP.then(function(file) { 13 | return file ? file.version : null; 14 | }); 15 | var versionP = q.all([blobIdP, previousIdP]) 16 | .spread(function(blobId, previousId) { 17 | return { 18 | userAccountId: userAccount.id, 19 | date: new Date(), 20 | blobId: blobId, 21 | creatorId: userAccount.id, 22 | previousId: previousId 23 | }; 24 | }); 25 | versionP = versionP.then(function(version) { 26 | version.id = Version.createHash(version); 27 | return version; 28 | }); 29 | var insertVersion = versionP.then(function(version) { 30 | return Version.insert(version).execWithin(tx); 31 | }); 32 | 33 | var fileIdP = q.all([fileP, versionP]).spread(function(file, version) { 34 | if (!file) { 35 | var splitPath = idOrPath.split('/'); 36 | var fileName = splitPath[splitPath.length - 1]; 37 | var newId = uuid.v1(); 38 | return self.createQuery(idOrPath, { 39 | id: newId, 40 | userAccountId: userAccount.id, 41 | name: fileName, 42 | version: version.id 43 | }).then(function(q) { 44 | return q.execWithin(tx); 45 | }).then(function() { 46 | return newId; 47 | }); 48 | } else { 49 | return file.id; 50 | } 51 | }); 52 | 53 | var updateFile = q.all([fileIdP, versionP]).spread( 54 | function(fileId, version) { 55 | var ins = FileVersion.insert({ 56 | fileId: fileId, 57 | versionId: version.id 58 | }).execWithin(tx); 59 | var up = File.whereUpdate({id: fileId}, {version: version.id}) 60 | .execWithin(tx); 61 | return q.all([ins, up]); 62 | }); 63 | q.all([insertVersion, updateFile]).then(function() { 64 | tx.commit(); 65 | return done(); 66 | }, function(err) { 67 | tx.rollback(); 68 | return done(err); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /examples/promises-compose-p.js: -------------------------------------------------------------------------------- 1 | var P = require('p-promise'), 2 | fn = require('when/function'); 3 | 4 | require('../lib/fakesP')(P.denodeify); 5 | 6 | // Assume Version = objectLift(version), File = objectLift(file) etc. 7 | module.exports = function upload(stream, idOrPath, tag, done) { 8 | var blob = blobManager.create(account); 9 | var tx = db.begin(); 10 | var blobIdP = blob.put(stream); 11 | var fileP = self.byUuidOrPath(idOrPath).get(); 12 | var previousIdP = fileP.then(function(file) { 13 | return file ? file.version : null; 14 | }); 15 | var versionP = P.spread([blobIdP, previousIdP], 16 | function(blobId, previousId) { 17 | return { 18 | userAccountId: userAccount.id, 19 | date: new Date(), 20 | blobId: blobId, 21 | creatorId: userAccount.id, 22 | previousId: previousId 23 | }; 24 | }); 25 | versionP = versionP.then(function(version) { 26 | version.id = Version.createHash(version); 27 | return version; 28 | }); 29 | var insertVersion = versionP.then(function(version) { 30 | return Version.insert(version).execWithin(tx); 31 | }); 32 | 33 | var fileIdP = P.spread([fileP, versionP], function(file, version) { 34 | if (!file) { 35 | var splitPath = idOrPath.split('/'); 36 | var fileName = splitPath[splitPath.length - 1]; 37 | var newId = uuid.v1(); 38 | return self.createQuery(idOrPath, { 39 | id: newId, 40 | userAccountId: userAccount.id, 41 | name: fileName, 42 | version: version.id 43 | }).then(function(q) { 44 | return q.execWithin(tx); 45 | }).then(function() { 46 | return newId; 47 | }); 48 | } else { 49 | return file.id; 50 | } 51 | }); 52 | 53 | var updateFile = P.spread([fileIdP, versionP], 54 | function(fileId, version) { 55 | var ins = FileVersion.insert({ 56 | fileId: fileId, 57 | versionId: version.id 58 | }).execWithin(tx); 59 | var up = File.whereUpdate({id: fileId}, {version: version.id}) 60 | .execWithin(tx); 61 | return P.all([ins, up]); 62 | }); 63 | P.spread([insertVersion, updateFile], function() { 64 | tx.commit(); 65 | return done(); 66 | }, function(err) { 67 | tx.rollback(); 68 | return done(err); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /examples/promises-compose-q.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | fn = require('when/function'); 3 | 4 | require('../lib/fakesP')(q.denodeify); 5 | 6 | // Assume Version = objectLift(version), File = objectLift(file) etc. 7 | module.exports = function upload(stream, idOrPath, tag, done) { 8 | var blob = blobManager.create(account); 9 | var tx = db.begin(); 10 | var blobIdP = blob.put(stream); 11 | var fileP = self.byUuidOrPath(idOrPath).get(); 12 | var previousIdP = fileP.then(function(file) { 13 | return file ? file.version : null; 14 | }); 15 | var versionP = q.spread([blobIdP, previousIdP], 16 | function(blobId, previousId) { 17 | return { 18 | userAccountId: userAccount.id, 19 | date: new Date(), 20 | blobId: blobId, 21 | creatorId: userAccount.id, 22 | previousId: previousId 23 | }; 24 | }); 25 | versionP = versionP.then(function(version) { 26 | version.id = Version.createHash(version); 27 | return version; 28 | }); 29 | var insertVersion = versionP.then(function(version) { 30 | return Version.insert(version).execWithin(tx); 31 | }); 32 | 33 | var fileIdP = q.spread([fileP, versionP], function(file, version) { 34 | if (!file) { 35 | var splitPath = idOrPath.split('/'); 36 | var fileName = splitPath[splitPath.length - 1]; 37 | var newId = uuid.v1(); 38 | return self.createQuery(idOrPath, { 39 | id: newId, 40 | userAccountId: userAccount.id, 41 | name: fileName, 42 | version: version.id 43 | }).then(function(q) { 44 | return q.execWithin(tx); 45 | }).then(function() { 46 | return newId; 47 | }); 48 | } else { 49 | return file.id; 50 | } 51 | }); 52 | 53 | var updateFile = q.spread([fileIdP, versionP], 54 | function(fileId, version) { 55 | var ins = FileVersion.insert({ 56 | fileId: fileId, 57 | versionId: version.id 58 | }).execWithin(tx); 59 | var up = File.whereUpdate({id: fileId}, {version: version.id}) 60 | .execWithin(tx); 61 | return q.all([ins, up]); 62 | }); 63 | q.spread([insertVersion, updateFile], function() { 64 | tx.commit(); 65 | return done(); 66 | }, function(err) { 67 | tx.rollback(); 68 | return done(err); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /examples/promises-kew.js: -------------------------------------------------------------------------------- 1 | var q = require('kew'); 2 | require('../lib/fakesP')(require('../lib/kew-lifter')); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobIdP = blob.put(stream); 8 | var fileP = self.byUuidOrPath(idOrPath).get(); 9 | var version, fileId, file; 10 | q.all([blobIdP, fileP]).then(function(all) { 11 | var blobId = all[0], fileV = all[1]; 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQueryCtxless(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-p.js: -------------------------------------------------------------------------------- 1 | 2 | var P = require('p-promise'); 3 | require('../lib/fakesP')(P.denodeify); 4 | 5 | module.exports = function upload(stream, idOrPath, tag, done) { 6 | var blob = blobManager.create(account); 7 | var tx = db.begin(); 8 | var blobIdP = blob.put(stream); 9 | var fileP = self.byUuidOrPath(idOrPath).get(); 10 | var version, fileId, file; 11 | P.spread([blobIdP, fileP], function(blobId, fileV) { 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQuery(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-q-generator.js: -------------------------------------------------------------------------------- 1 | var q = require('q'); 2 | require('../lib/fakesP')(q.denodeify); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | q.spawn(function* () { 6 | try { 7 | var blob = blobManager.create(account); 8 | var tx = db.begin(); 9 | var blobId = yield blob.put(stream); 10 | var file = yield self.byUuidOrPath(idOrPath).get(); 11 | var previousId = file ? file.version : null; 12 | version = { 13 | userAccountId: userAccount.id, 14 | date: new Date(), 15 | blobId: blobId, 16 | creatorId: userAccount.id, 17 | previousId: previousId, 18 | }; 19 | version.id = Version.createHash(version); 20 | yield Version.insert(version).execWithin(tx); 21 | if (!file) { 22 | var splitPath = idOrPath.split('/'); 23 | var fileName = splitPath[splitPath.length - 1]; 24 | file = { 25 | id: uuid.v1(), 26 | userAccountId: userAccount.id, 27 | name: fileName, 28 | version: version.id 29 | } 30 | var query = yield self.createQuery(idOrPath, file); 31 | yield query.execWithin(tx); 32 | } 33 | yield FileVersion.insert({fileId: file.id, versionId: version.id}) 34 | .execWithin(tx); 35 | yield File.whereUpdate({id: file.id}, {version: version.id}) 36 | .execWithin(tx); 37 | tx.commit(); 38 | return done(); 39 | } catch (err) { 40 | tx.rollback(); 41 | return done(err); 42 | } 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /examples/promises-q.js: -------------------------------------------------------------------------------- 1 | 2 | var q = require('q'); 3 | require('../lib/fakesP')(q.denodeify); 4 | 5 | module.exports = function upload(stream, idOrPath, tag, done) { 6 | var blob = blobManager.create(account); 7 | var tx = db.begin(); 8 | var blobIdP = blob.put(stream); 9 | var fileP = self.byUuidOrPath(idOrPath).get(); 10 | var version, fileId, file; 11 | q.spread([blobIdP, fileP], function(blobId, fileV) { 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQuery(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-tildeio-rsvp.js: -------------------------------------------------------------------------------- 1 | var rsvp = require('rsvp'); 2 | require('../lib/fakesP')(rsvp.denodeify); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var blob = blobManager.create(account); 6 | var tx = db.begin(); 7 | var blobIdP = blob.put(stream); 8 | var fileP = self.byUuidOrPath(idOrPath).get(); 9 | var version, fileId, file; 10 | rsvp.all([blobIdP, fileP]).then(function(all) { 11 | var blobId = all[0], fileV = all[1]; 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQueryCtxless(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/promises-when.js: -------------------------------------------------------------------------------- 1 | var when = require('when'); 2 | require('../lib/fakesP')(require('when/node/function').lift); 3 | 4 | 5 | module.exports = function upload(stream, idOrPath, tag, done) { 6 | var blob = blobManager.create(account); 7 | var tx = db.begin(); 8 | var blobIdP = blob.put(stream); 9 | var fileP = self.byUuidOrPath(idOrPath).get(); 10 | var version, fileId, file; 11 | when([blobIdP, fileP]).spread(function(blobId, fileV) { 12 | file = fileV; 13 | var previousId = file ? file.version : null; 14 | version = { 15 | userAccountId: userAccount.id, 16 | date: new Date(), 17 | blobId: blobId, 18 | creatorId: userAccount.id, 19 | previousId: previousId, 20 | }; 21 | version.id = Version.createHash(version); 22 | return Version.insert(version).execWithin(tx); 23 | }).then(function() { 24 | if (!file) { 25 | var splitPath = idOrPath.split('/'); 26 | var fileName = splitPath[splitPath.length - 1]; 27 | var newId = uuid.v1(); 28 | return self.createQuery(idOrPath, { 29 | id: newId, 30 | userAccountId: userAccount.id, 31 | name: fileName, 32 | version: version.id 33 | }).then(function(q) { 34 | return q.execWithin(tx); 35 | }).then(function() { 36 | return newId; 37 | }); 38 | } else { 39 | return file.id; 40 | } 41 | }).then(function(fileIdV) { 42 | fileId = fileIdV; 43 | return FileVersion.insert({ 44 | fileId: fileId, 45 | versionId: version.id 46 | }).execWithin(tx); 47 | }).then(function() { 48 | return File.whereUpdate({id: fileId}, {version: version.id}) 49 | .execWithin(tx); 50 | }).then(function() { 51 | tx.commit(); 52 | return done(); 53 | }, function(err) { 54 | tx.rollback(); 55 | return done(err); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /examples/rx.js: -------------------------------------------------------------------------------- 1 | var Rx = require('rx'); 2 | 3 | require('../lib/fakes'); 4 | 5 | module.exports = function upload(stream, idOrPath, tag, done) { 6 | 7 | var blob = blobManager.create(account), 8 | tx = db.begin(), 9 | shouldRollback = false; 10 | 11 | blobPutAsObs(blob, stream) 12 | .selectMany(selectFile(idOrPath)) 13 | .doAction(function() { shouldRollback = true; }) 14 | .selectMany(insertVersion(tx, idOrPath, tag)) 15 | .selectMany(insertFile(tx)) 16 | .selectMany(updateFileVersion(tx)) 17 | .selectMany(commitDBAction) 18 | .subscribe( 19 | onSuccessfulCommit, 20 | onCommitError 21 | ); 22 | 23 | function onSuccessfulCommit(commitResultArgs) { 24 | done.apply(null, commitResultArgs); 25 | }; 26 | 27 | function onCommitError(err) { 28 | if(shouldRollback) 29 | tx.rollback(); 30 | done(err); 31 | } 32 | } 33 | 34 | function blobPutAsObs(blob, stream) { 35 | 36 | return Rx.Observable.create(function(observer) { 37 | blob.put(stream, function(err, iBlobId) { 38 | 39 | if(err) return observer.onError(err); 40 | 41 | observer.onNext(iBlobId); 42 | observer.onCompleted(); 43 | }); 44 | }); 45 | } 46 | 47 | function selectFile(idOrPath) { 48 | 49 | return function(blobId) { 50 | 51 | return Rx.Observable.create(function(observer) { 52 | 53 | self.byUuidOrPath(idOrPath).get(function(err, iFile) { 54 | 55 | if(err) return observer.onError(err); 56 | 57 | observer.onNext([blobId, iFile]); 58 | observer.onCompleted(); 59 | }); 60 | }); 61 | } 62 | } 63 | 64 | function insertVersion(tx, idOrPath, tag) { 65 | 66 | return function(blobIdAndFile) { 67 | 68 | 69 | var blobId = blobIdAndFile[0], 70 | iFile = blobIdAndFile[1], 71 | previousId = iFile ? iFile.version : null, 72 | version = { 73 | userAccountId: userAccount.id, 74 | date: new Date(), 75 | blobId: blobId, 76 | creatorId: userAccount.id, 77 | previousId: previousId, 78 | mergedId: null, 79 | mergeType: 'mine', 80 | comment: '', 81 | tag: tag 82 | }, 83 | slashIndex = idOrPath.lastIndexOf('/'), 84 | fileName = idOrPath.substring(slashIndex), 85 | newId = uuid.v1(), 86 | createFile = createFileAsObs(self, fileName, newId, version, 87 | idOrPath), 88 | doQuery = executeQuery(tx, newId), 89 | createFileObs = createFile.selectMany(doQuery), 90 | fileExistsObs = Rx.Observable.returnValue(newId); 91 | 92 | version.id = Version.createHash(version); 93 | 94 | return Rx.Observable.ifThen( 95 | function() { return !iFile; }, 96 | createFileObs, 97 | fileExistsObs 98 | ) 99 | .select(function() { 100 | return [newId, version.id]; 101 | }); 102 | } 103 | } 104 | 105 | function createFileAsObs(self, fileName, newId, version, idOrPath) { 106 | 107 | return Rx.Observable.create(function(observer) { 108 | 109 | 110 | var query = { 111 | id: newId, 112 | userAccountId: userAccount.id, 113 | type: 'file', 114 | name: fileName, 115 | version: version.id 116 | }; 117 | 118 | self.createQuery(idOrPath, query, function (err, q) { 119 | 120 | if(err) return observer.onError(err); 121 | 122 | observer.onNext(q); 123 | observer.onCompleted(); 124 | }) 125 | }); 126 | } 127 | 128 | function executeQuery(tx, newId) { 129 | 130 | return function(q) { 131 | 132 | return Rx.Observable.create(function(observer) { 133 | 134 | q.execWithin(tx, function(err) { 135 | 136 | if(err) return observer.onError(err); 137 | 138 | observer.onNext(newId); 139 | observer.onCompleted(); 140 | }); 141 | }); 142 | } 143 | } 144 | 145 | function insertFile(tx) { 146 | 147 | return function(fileIdAndVersion) { 148 | 149 | var fileId = fileIdAndVersion[0], 150 | versionId = fileIdAndVersion[1], 151 | insert = { 152 | fileId: fileId, 153 | versionId: versionId 154 | }; 155 | return Rx.Observable.create(function(observer) { 156 | 157 | FileVersion.insert(insert) 158 | .execWithin(tx, function(err) { 159 | 160 | if(err) return observer.onError(err); 161 | 162 | observer.onNext(fileIdAndVersion); 163 | observer.onCompleted(); 164 | }); 165 | }); 166 | } 167 | } 168 | 169 | function updateFileVersion(tx) { 170 | 171 | return function(fileIdAndVersion) { 172 | 173 | var fileId = fileIdAndVersion[0], 174 | versionId = fileIdAndVersion[1], 175 | file = { id: fileId }, 176 | version = { version: versionId }; 177 | 178 | return Rx.Observable.create(function(observer) { 179 | File 180 | .whereUpdate(file, version) 181 | .execWithin(tx, function(err) { 182 | 183 | if(err) return observer.onError(err); 184 | 185 | observer.onNext(tx); 186 | observer.onCompleted(); 187 | }); 188 | }); 189 | } 190 | } 191 | 192 | function commitDBAction(tx) { 193 | 194 | return Rx.Observable.create(function(observer) { 195 | 196 | // Not sure of the commit callback API, so 197 | // just pass whatever arguments back as the 198 | // next message and let the subscriber apply 199 | // them to the done callback. 200 | tx.commit(function() { 201 | observer.onNext(arguments); 202 | observer.onCompleted(); 203 | }); 204 | }); 205 | } 206 | -------------------------------------------------------------------------------- /examples/src-stratifiedjs.sjs: -------------------------------------------------------------------------------- 1 | require('../lib/fakesSJS-dst.js'); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, done) { 4 | var blob = blobManager.create(account); 5 | var blobId = blob.put(stream); 6 | var file = self.byUuidOrPath(idOrPath).get(); 7 | var previousId = file ? file.version : null; 8 | var version = { 9 | userAccountId: userAccount.id, 10 | date: new Date(), 11 | blobId: blobId, 12 | creatorId: userAccount.id, 13 | previousId: previousId 14 | }; 15 | version.id = Version.createHash(version); 16 | var tx = db.begin(); 17 | 18 | try { 19 | Version.insert(version).execWithin(tx); 20 | 21 | var fileId; 22 | if (!file) { 23 | var splitPath = idOrPath.split('/'); 24 | var fileName = splitPath[splitPath.length - 1]; 25 | fileId = uuid.v1(); 26 | self.createQuery(idOrPath, 27 | { 28 | id: fileId, 29 | userAccountId: userAccount.id, 30 | name: fileName, 31 | version: version.id 32 | }) 33 | .execWithin(tx); 34 | } 35 | else 36 | fileId = file.id; 37 | 38 | FileVersion.insert({fileId: fileId,versionId: version.id}) 39 | .execWithin(tx); 40 | File.whereUpdate({id: fileId}, {version: version.id}) 41 | .execWithin(tx); 42 | } catch(e) { 43 | tx.rollback(); 44 | done(e); // if the caller were SJS, we would just throw() 45 | } 46 | tx.commit(); 47 | done(); // if the caller were SJS, we would just return 48 | } -------------------------------------------------------------------------------- /examples/src-streamline._js: -------------------------------------------------------------------------------- 1 | require('../lib/fakes'); 2 | 3 | module.exports = function upload(stream, idOrPath, tag, _) { 4 | var blob = blobManager.create(account); 5 | var tx = db.begin(); 6 | try { 7 | var blobId = blob.put(stream, _); 8 | var file = self.byUuidOrPath(idOrPath).get(_); 9 | var previousId = file ? file.version : null; 10 | var version = { 11 | userAccountId: userAccount.id, 12 | date: new Date(), 13 | blobId: blobId, 14 | creatorId: userAccount.id, 15 | previousId: previousId, 16 | }; 17 | version.id = Version.createHash(version); 18 | Version.insert(version).execWithin(tx, _); 19 | if (!file) { 20 | var splitPath = idOrPath.split('/'); 21 | var fileName = splitPath[splitPath.length - 1]; 22 | var newId = uuid.v1(); 23 | var file = { 24 | id: newId, 25 | userAccountId: userAccount.id, 26 | name: fileName, 27 | version: version.id 28 | } 29 | var q = self.createQuery(idOrPath, file, _); 30 | q.execWithin(tx, _); 31 | } 32 | FileVersion.insert({fileId: file.id,versionId: version.id}) 33 | .execWithin(tx, _); 34 | File.whereUpdate({id: file.id}, {version: version.id}) 35 | .execWithin(tx, _); 36 | tx.commit(_); 37 | } catch (err) { 38 | tx.rollback(); 39 | throw err; //Error(err); 40 | } 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /examples/thunks-generator-co.js: -------------------------------------------------------------------------------- 1 | var co = require("co"); 2 | require('../lib/fakesC'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var tx = db.begin(); 6 | var blob = blobManager.create(account) 7 | co(function* upload() { 8 | var file = yield self.byUuidOrPath(idOrPath).get(); 9 | var blobId = yield blob.put(stream); 10 | var previousId = file ? file.version : null 11 | var version = { 12 | userAccountId: userAccount.id, 13 | date: new Date(), 14 | blobId: blobId, 15 | creatorId: userAccount.id, 16 | previousId: previousId 17 | } 18 | version.id = Version.createHash(version) 19 | yield Version.insert(version).execWithin(tx); 20 | if (!file) { 21 | var splitPath = idOrPath.split("/") 22 | var fileName = splitPath[splitPath.length - 1] 23 | file = { 24 | id: uuid.v1(), 25 | userAccountId: userAccount.id, 26 | name: fileName, 27 | version: version.id 28 | }; 29 | var query = yield self.createQuery(idOrPath, file); 30 | yield query.execWithin(tx) 31 | } 32 | yield FileVersion.insert({ fileId: file.id, versionId: version.id }) 33 | .execWithin(tx); 34 | yield File.whereUpdate({ id: file.id }, { version: version.id }) 35 | .execWithin(tx); 36 | yield tx.commit(); 37 | })(function(err) { 38 | if (err) tx.rollback() 39 | done(err); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /examples/thunks-generator-gens.js: -------------------------------------------------------------------------------- 1 | var async = require("gens"); 2 | require('../lib/fakes'); 3 | 4 | module.exports = function upload(stream, idOrPath, tag, done) { 5 | var tx = db.begin() 6 | uploadTransaction(tx, stream, idOrPath, tag, function (err) { 7 | if (err) tx.rollback() 8 | done(err); 9 | }) 10 | } 11 | 12 | var uploadTransaction = async(function* upload(tx, stream, idOrPath, tag) { 13 | var blob = blobManager.create(account) 14 | var file = yield self.byUuidOrPath(idOrPath).get; 15 | var blobId = yield blob.put.bind(blob, stream); 16 | var previousId = file ? file.version : null 17 | var version = { 18 | userAccountId: userAccount.id, 19 | date: new Date(), 20 | blobId: blobId, 21 | creatorId: userAccount.id, 22 | previousId: previousId 23 | } 24 | version.id = Version.createHash(version) 25 | yield Version.insert(version).execWithin.bind(null, tx); 26 | if (!file) { 27 | var splitPath = idOrPath.split("/") 28 | var fileName = splitPath[splitPath.length - 1] 29 | file = { 30 | id: uuid.v1(), 31 | userAccountId: userAccount.id, 32 | name: fileName, 33 | version: version.id 34 | }; 35 | var query = yield self.createQuery.bind(self, idOrPath, file); 36 | yield query.execWithin.bind(query, tx) 37 | } 38 | yield FileVersion.insert({ fileId: file.id, versionId: version.id }) 39 | .execWithin.bind(null, tx); 40 | yield File.whereUpdate({ id: file.id }, { version: version.id }) 41 | .execWithin.bind(null, tx); 42 | yield tx.commit 43 | }); 44 | -------------------------------------------------------------------------------- /latest-results.md: -------------------------------------------------------------------------------- 1 | Date: 2014-08-27 2 | 3 | # Performance 4 | 5 | ``` 6 | results for 10000 parallel executions, 1 ms per I/O op 7 | 8 | file time(ms) memory(MB) 9 | callbacks-flattened-class-ctx.js 188 14.47 10 | callbacks-flattened-class.js 282 30.05 11 | promises-bluebird-generator-ctx.js 308 28.23 12 | callbacks-original.js 387 38.01 13 | callbacks-flattened.js 397 41.76 14 | callbacks-flattened-passing.js 399 35.30 15 | promises-bluebird-generator.js 414 40.28 16 | callbacks-catcher.js 448 50.49 17 | callbacks-generator-suspend.js 465 44.84 18 | promises-bluebird-ctx.js 499 49.55 19 | promises-bluebird.js 550 55.25 20 | dst-streamline.js 557 53.96 21 | promises-p.js 561 48.85 22 | thunks-generator-gens.js 602 41.94 23 | callbacks-deferred-queue.js 663 56.96 24 | promises-when.js 684 86.81 25 | promises-tildeio-rsvp.js 766 81.79 26 | promises-compose-bluebird.js 767 54.73 27 | promises-compose-p.js 788 74.00 28 | callbacks-generator-genny.js 831 68.89 29 | dst-callbacks-generator-suspend-traceur.js 1045 51.07 30 | dst-callbacks-generator-genny-traceur.js 1166 71.77 31 | callbacks-async-waterfall.js 1218 72.93 32 | promises-kew.js 1704 138.16 33 | dst-stratifiedjs-compiled.js 2518 133.43 34 | rx.js 3243 271.58 35 | promises-q.js 12172 594.73 36 | dst-promises-q-generator-traceur.js 18330 469.77 37 | promises-q-generator.js 34646 558.55 38 | promises-compose-q.js 98680 1076.06 39 | dst-streamline-fibers.js N/A N/A 40 | promises-bluebird-spawn.js N/A N/A 41 | dst-thunks-generator-co-traceur.js N/A N/A 42 | thunks-generator-co.js N/A N/A 43 | fibrous.js N/A N/A 44 | 45 | Platform info: 46 | Linux 3.11.0-18-generic x64 47 | Node.JS 0.11.13 48 | V8 3.25.30 49 | Intel(R) Core(TM) i5 CPU 750 @ 2.67GHz × 4 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /lib/catcher.js: -------------------------------------------------------------------------------- 1 | 2 | exports.longStackSupport = global.longStackSupport; 3 | 4 | 5 | function invoke(ctx, cb, value, myhandler) { 6 | try { 7 | cb.call(ctx, value); // no error 8 | } catch (e) { 9 | if (myhandler) 10 | myhandler.call(ctx, e); 11 | else 12 | console.error(e); 13 | } 14 | } 15 | 16 | module.exports = function() { 17 | var self = {}; 18 | var notCaught = true, myhandler; 19 | self.try = function $try(cb) { 20 | if (exports.longStackSupport) { 21 | var ex = {}; 22 | Error.captureStackTrace(ex); 23 | } 24 | return function wrapper(err, value) { 25 | if (err) { 26 | if (notCaught) { 27 | notCaught = false; 28 | if (err.stack && ex) { 29 | var asyncStackRaw = 30 | ex.stack.substr(ex.stack.indexOf('\n')); 31 | err.stack += '\nFrom previous event:' 32 | + asyncStackRaw; 33 | } 34 | if (myhandler) myhandler(err); 35 | else console.error(err); 36 | } 37 | } 38 | else if (myhandler) 39 | invoke(this, cb, value, myhandler); 40 | else cb(value); 41 | } 42 | } 43 | self.catch = function $catch(handler) { 44 | myhandler = handler 45 | }; 46 | return self; 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /lib/dummy.js: -------------------------------------------------------------------------------- 1 | // A typical node callback function 2 | // with the callback at the Nth position 3 | exports.dummy = function dummy(n) { 4 | return function dummy_n() { 5 | var cb = arguments[n - 1]; 6 | if (global.asyncTime) 7 | setTimeout(cb, global.asyncTime || 100); 8 | else 9 | process.nextTick(cb); 10 | } 11 | } 12 | 13 | // A throwing callback function 14 | exports.dummyt = function dummyt(n) { 15 | return function dummy_throwing_n() { 16 | var cb = arguments[n - 1]; 17 | if (global.testThrow) 18 | throw(new Error("Exception happened")); 19 | setTimeout(function throwTimeout() { 20 | if (global.testThrowAsync) { 21 | throw(new Error("Exception happened")); 22 | } else if (global.testError) { 23 | return cb(new Error("Error happened")); 24 | } 25 | else cb(); 26 | }, global.asyncTime || 100); 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/fakemaker.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function fakemaker(dummy, dummyt, wrap) { 3 | 4 | var dummy_2 = dummy(2), 5 | dummy_1 = dummy(1); 6 | 7 | var dummyt_2, dummyt_1; 8 | 9 | if (global.testError || global.testThrow 10 | || global.testThrowAsync) { 11 | dummyt_2 = dummyt(2); 12 | dummyt_1 = dummyt(1); 13 | } else { 14 | dummyt_2 = dummy_2; 15 | dummyt_1 = dummy_1; 16 | } 17 | 18 | // a queryish object with all 19 | // kinds of functions 20 | function queryish() { 21 | return { 22 | execWithin: dummy_2, 23 | exec: dummy_1, 24 | get: dummy_1, 25 | all: dummy_1, 26 | }; 27 | } 28 | 29 | // a queryish object with functions 30 | // that throw 31 | function queryisht() { 32 | return { 33 | execWithin: dummyt_2, 34 | exec: dummyt_1, 35 | get: dummyt_1, 36 | all: dummyt_1, 37 | }; 38 | } 39 | 40 | global.uuid = { v1: function v1() {} }; 41 | 42 | global.userAccount = { }; 43 | 44 | global.account = { }; 45 | 46 | global.blobManager = { 47 | create: function create() { 48 | return { 49 | put: dummy_2, 50 | } 51 | } 52 | }; 53 | 54 | var cqQueryish = queryish(); 55 | 56 | global.self = { 57 | byUuidOrPath: queryish, 58 | createQuery: wrap(function createQuery(x, y, cb, ctx) { 59 | cb.call(ctx, null, cqQueryish); 60 | }), 61 | createQueryCtxless: wrap(function createQuery(x, y, cb) { 62 | cb.call(this, null, cqQueryish); 63 | }) 64 | }; 65 | 66 | 67 | global.File = { 68 | insert: queryish, 69 | whereUpdate: queryish 70 | }; 71 | 72 | global.FileVersion = { 73 | insert: queryisht 74 | }; 75 | 76 | global.Version = { 77 | createHash: function createHash(v) { return 1; }, 78 | insert: queryish 79 | }; 80 | 81 | global.db = { 82 | begin: function begin() { 83 | return { 84 | commit: dummy_1, 85 | rollback: dummy_1, 86 | }; 87 | } 88 | }; 89 | 90 | }; 91 | 92 | 93 | -------------------------------------------------------------------------------- /lib/fakes-ctx.js: -------------------------------------------------------------------------------- 1 | var timers = require('./timers-ctx'); 2 | 3 | var fakemaker = require('./fakemaker'); 4 | 5 | var f = {}; 6 | f.dummy = function dummy(n) { 7 | return function dummy_n() { 8 | var cb = arguments[n - 1], 9 | ctx = arguments[n]; 10 | //console.log(cb, ctx); 11 | 12 | timers.setTimeout(cb, ctx, global.asyncTime || 100); 13 | } 14 | } 15 | 16 | // A throwing callback function 17 | f.dummyt = function dummyt(n) { 18 | return function dummy_throwing_n() { 19 | var cb = arguments[n - 1], 20 | ctx = arguments[n]; 21 | if (global.testThrow) 22 | throw(new Error("Exception happened")); 23 | setTimeout(function throwTimeout() { 24 | if (global.testThrowAsync) { 25 | throw(new Error("Exception happened")); 26 | } else if (global.testError) { 27 | return cb.call(ctx, new Error("Error happened")); 28 | } 29 | else cb.call(ctx); 30 | }, global.asyncTime || 100); 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | fakemaker(f.dummy, f.dummyt, function wrap_identity(f) { return f; }); 38 | 39 | 40 | -------------------------------------------------------------------------------- /lib/fakes.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var fakemaker = require('./fakemaker'), 4 | f = require('./dummy'); 5 | 6 | fakemaker(f.dummy, f.dummyt, function wrap_identity(f) { return f; }); 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/fakesC.js: -------------------------------------------------------------------------------- 1 | 2 | var co = require('co'); 3 | 4 | var f = require('./dummy'); 5 | 6 | var makefakes = require('./fakemaker'); 7 | 8 | // Continuable versions made with co.wrap 9 | function dummyC(n) { 10 | return co.wrap(f.dummy(n)); 11 | } 12 | function dummytC(n) { 13 | return co.wrap(f.dummyt(n)); 14 | } 15 | 16 | makefakes(dummyC, dummytC, co.wrap); 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/fakesO.js: -------------------------------------------------------------------------------- 1 | 2 | var Rx = require('rx'); 3 | var f = require('./dummy'); 4 | 5 | var dummy1 = f.dummy(1), 6 | dummyt1 = f.dummyt(1); 7 | 8 | // Observable wrapper 9 | function dummyObsWrap(fn) { 10 | return function() { 11 | return Rx.Observable.create(function(observer) { 12 | fn(function(err, res) { 13 | if(err) 14 | return observer.onError(err); 15 | observer.onNext(res); 16 | observer.onCompleted(); 17 | }); 18 | }); 19 | } 20 | } 21 | function dummyO() { 22 | return dummyObsWrap(dummy(1)); 23 | } 24 | function dummytO() { 25 | return dummyObsWrap(dummyt(1)); 26 | } 27 | 28 | makefakes(dummyO, dummytO, dummyObsWrap); 29 | 30 | -------------------------------------------------------------------------------- /lib/fakesP-ctx.js: -------------------------------------------------------------------------------- 1 | var timers = require('./timers-ctx'); 2 | 3 | var fakemaker = require('./fakemaker'); 4 | 5 | var f = {}; 6 | f.dummy = function dummy(n) { 7 | return function dummy_n() { 8 | var cb = arguments[n - 1], 9 | ctx = arguments[n]; 10 | timers.setTimeout(cb, ctx, global.asyncTime || 100); 11 | } 12 | } 13 | 14 | // A throwing callback function 15 | f.dummyt = function dummyt(n) { 16 | return function dummy_throwing_n() { 17 | var cb = arguments[n - 1], 18 | ctx = arguments[n]; 19 | if (global.testThrow) 20 | throw(new Error("Exception happened")); 21 | setTimeout(function throwTimeout() { 22 | if (global.testThrowAsync) { 23 | throw(new Error("Exception happened")); 24 | } else if (global.testError) { 25 | return cb.call(ctx, new Error("Error happened")); 26 | } 27 | else cb.call(ctx); 28 | }, global.asyncTime || 100); 29 | } 30 | } 31 | 32 | 33 | 34 | //Currently promisifies only Node style callbacks 35 | //var lifter = require('bluebird').promisify; 36 | 37 | var Promise = require('bluebird'); 38 | 39 | function nodeback(err, result) { 40 | if (err == null) this.fulfill(result); 41 | else this.reject(err); 42 | } 43 | 44 | function lifter(f) { 45 | return function lifted(a1, a2, a3, a4, a5) { 46 | "use strict"; 47 | var len = arguments.length; 48 | var deferred = Promise.pending(); 49 | try { 50 | switch(len) { 51 | case 1: f(a1, nodeback, deferred); break; 52 | case 2: f(a1, a2, nodeback, deferred); break; 53 | case 0: f(nodeback, deferred); break; 54 | case 3: f(a1, a2, a3, nodeback, deferred); break; 55 | case 4: f(a1, a2, a3, a4, nodeback, deferred); break; 56 | case 5: f(a1, a2, a3, a4, a5, nodeback, deferred); break; 57 | } 58 | } catch (err) { deferred.reject(err); } 59 | return deferred.promise; 60 | } 61 | } 62 | 63 | // A function taking n values or promises and returning 64 | // a promise 65 | function dummyP(n) { 66 | return function lifted() { 67 | var deferred = Promise.pending(); 68 | timers.setTimeout(nodeback, deferred, global.asyncTime || 100); 69 | return deferred.promise; 70 | } 71 | } 72 | 73 | // Throwing version of above 74 | function dummytP(n) { 75 | return lifter(f.dummyt(n)); 76 | } 77 | 78 | fakemaker(dummyP, dummytP, lifter); 79 | 80 | -------------------------------------------------------------------------------- /lib/fakesP.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(lifter) { 3 | if (global.usePromiseLifter) 4 | var lifter = global.usePromiseLifter; 5 | 6 | var f = require('./dummy'); 7 | 8 | var makefakes = require('./fakemaker'); 9 | 10 | // A function taking n values or promises and returning 11 | // a promise 12 | function dummyP(n) { 13 | return lifter(f.dummy(n)); 14 | } 15 | 16 | // Throwing version of above 17 | function dummytP(n) { 18 | return lifter(f.dummyt(n)); 19 | } 20 | 21 | makefakes(dummyP, dummytP, lifter); 22 | } 23 | -------------------------------------------------------------------------------- /lib/fakesSJS-dst.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var f,makefakes;function wrap(f){return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){return __oni_rt.Return(function (x,y){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return f(x,y,resume);},8),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},10)])});},12)])}function dummySJS0(){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummy(1);return __oni_rt.Return(function (){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return inner(resume);},19),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},21)])});},17)])}function dummySJS1(){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummy(2);return __oni_rt.Return(function (x){var err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){return inner(x,resume);},30),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},32)])});},28)])}function dummySJS(n){if(n === 1){return dummySJS0();}if(n === 2){return dummySJS1();}}function dummytSJS(n){var inner;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Nb(function(){inner=f.dummyt(n);return __oni_rt.Return(function (){var args,err,val;return __oni_rt.exseq(arguments,this,'lib/fakesSJS-src.sjs',[1,__oni_rt.Suspend(function(__oni_env,resume){return __oni_rt.ex(__oni_rt.Nb(function(){args=Array.prototype.slice.apply(this.aobj);args.push(resume);inner.apply(this,args);},0),__oni_env)}, function() {err=arguments[0];val=arguments[1];}),__oni_rt.Nb(function(){if(err){throw err;}return __oni_rt.Return(val);},51)])});},44)])}__oni_rt.exseq(this.arguments,this,'lib/fakesSJS-src.sjs',[24,__oni_rt.Sc(3,function(_oniX){return f=_oniX;},__oni_rt.C(function(){return require('./dummy.js')},1)),__oni_rt.Sc(5,function(_oniX){return makefakes=_oniX;},__oni_rt.C(function(){return require('./fakemaker.js')},3)),__oni_rt.Nb(function(){return makefakes(dummySJS,dummytSJS,wrap,global);},56)]) 4 | 5 | -------------------------------------------------------------------------------- /lib/fakesSJS-src.sjs: -------------------------------------------------------------------------------- 1 | var f = require('./dummy.js'); 2 | 3 | var makefakes = require('./fakemaker.js'); 4 | 5 | function wrap(f) { 6 | return function(x, y) { 7 | waitfor(var err, val) { 8 | f(x, y, resume); 9 | } 10 | if (err) throw err; 11 | return val; 12 | }; 13 | } 14 | 15 | function dummySJS0() { 16 | var inner = f.dummy(1); 17 | return function() { 18 | waitfor (var err, val) { 19 | inner(resume); 20 | } 21 | if (err) throw err; 22 | return val; 23 | } 24 | } 25 | 26 | function dummySJS1() { 27 | var inner = f.dummy(2); 28 | return function(x) { 29 | waitfor (var err, val) { 30 | inner(x, resume); 31 | } 32 | if (err) throw err; 33 | return val; 34 | } 35 | } 36 | 37 | function dummySJS(n) { 38 | if (n === 1) return dummySJS0(); 39 | if (n === 2) return dummySJS1(); 40 | } 41 | 42 | function dummytSJS(n) { 43 | var inner = f.dummyt(n); 44 | return function() { 45 | waitfor(var err, val) { 46 | var args = Array.prototype.slice.apply(arguments); 47 | args.push(resume); 48 | inner.apply(this, args); 49 | } 50 | 51 | if (err) throw err; 52 | return val; 53 | } 54 | } 55 | 56 | makefakes(dummySJS, dummytSJS, wrap, global); 57 | -------------------------------------------------------------------------------- /lib/kew-lifter.js: -------------------------------------------------------------------------------- 1 | 2 | var q = require('kew'); 3 | var slicer = [].slice; 4 | function lifter(nodefn) { 5 | return function() { 6 | var p = q.defer(); 7 | arguments[arguments.length++] = function(err, res) { 8 | if (err) p.reject(err); 9 | else p.resolve(res) 10 | }; 11 | nodefn.apply(this, arguments); 12 | return p; 13 | } 14 | } 15 | 16 | module.exports = lifter; 17 | -------------------------------------------------------------------------------- /lib/promiseSupport.js: -------------------------------------------------------------------------------- 1 | var when = require('when'); 2 | var fn = require('when/function'); 3 | 4 | 5 | 6 | exports.ternary = fn.lift(function(truthy, iftrue, iffalse) { 7 | return truthy ? iftrue: iffalse; 8 | }) 9 | 10 | exports.not = function not(truthyP) { 11 | return when(truthyP).then(function(truthyVal) { 12 | return !truthyVal; 13 | }); 14 | } 15 | 16 | exports.allObject = function allObject(objWithPromises) { 17 | return when(objWithPromises).then(function(objWithPromises) { 18 | var keys = Object.keys(objWithPromises); 19 | return when.all(keys.map(function(key) { 20 | return objWithPromise; 21 | })).then(function(vals) { 22 | var init = {}; 23 | for (var k = 0; k < keys.length; ++k) { 24 | init[keys[k]] = vals[k]; 25 | } 26 | return init; 27 | }); 28 | }); 29 | } 30 | 31 | exports.set = fn.lift(function(obj, values) { 32 | for (var key in values) 33 | obj[key] = values[key]; 34 | return obj; 35 | }); 36 | 37 | exports.if = function ifP (truthyP, fnTrue, fnFalse) { 38 | return truthyP.then(function(truthy) { 39 | if (truthy) return fnTrue(); 40 | else return fnFalse(); 41 | }); 42 | } 43 | 44 | exports.get = fn.lift(function (obj, key) { 45 | return obj[key]; 46 | }); 47 | 48 | exports.eventuallyCall = fn.lift(function(obj, fnkey) { 49 | var args = [].slice.call(arguments, 2); 50 | obj[fnkey].apply(obj, args); 51 | }); 52 | -------------------------------------------------------------------------------- /lib/timers-ctx.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | var Timer = process.binding('timer_wrap').Timer; 23 | var L = require('_linklist'); 24 | var assert = require('assert').ok; 25 | 26 | var kOnTimeout = Timer.kOnTimeout | 0; 27 | 28 | // Timeout values > TIMEOUT_MAX are set to 1. 29 | var TIMEOUT_MAX = 2147483647; // 2^31-1 30 | 31 | 32 | var debug = require('util').debuglog('timer'); 33 | 34 | 35 | // IDLE TIMEOUTS 36 | // 37 | // Because often many sockets will have the same idle timeout we will not 38 | // use one timeout watcher per item. It is too much overhead. Instead 39 | // we'll use a single watcher for all sockets with the same timeout value 40 | // and a linked list. This technique is described in the libev manual: 41 | // http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 42 | 43 | // Object containing all lists, timers 44 | // key = time in milliseconds 45 | // value = list 46 | var lists = {}; 47 | 48 | // the main function - creates lists on demand and the watchers associated 49 | // with them. 50 | function insert(item, msecs) { 51 | item._idleStart = Timer.now(); 52 | item._idleTimeout = msecs; 53 | 54 | if (msecs < 0) return; 55 | 56 | var list; 57 | 58 | if (lists[msecs]) { 59 | list = lists[msecs]; 60 | } else { 61 | list = new Timer(); 62 | list.start(msecs, 0); 63 | 64 | L.init(list); 65 | 66 | lists[msecs] = list; 67 | list.msecs = msecs; 68 | list[kOnTimeout] = listOnTimeout; 69 | } 70 | 71 | L.append(list, item); 72 | assert(!L.isEmpty(list)); // list is not empty 73 | } 74 | 75 | function listOnTimeout() { 76 | var msecs = this.msecs; 77 | var list = this; 78 | 79 | debug('timeout callback %d', msecs); 80 | 81 | var now = Timer.now(); 82 | debug('now: %s', now); 83 | 84 | var first; 85 | while (first = L.peek(list)) { 86 | var diff = now - first._idleStart; 87 | if (diff < msecs) { 88 | list.start(msecs - diff, 0); 89 | debug('%d list wait because diff is %d', msecs, diff); 90 | return; 91 | } else { 92 | L.remove(first); 93 | assert(first !== L.peek(list)); 94 | 95 | if (!first._onTimeout) continue; 96 | 97 | // v0.4 compatibility: if the timer callback throws and the 98 | // domain or uncaughtException handler ignore the exception, 99 | // other timers that expire on this tick should still run. 100 | // 101 | // https://github.com/joyent/node/issues/2631 102 | var domain = first.domain; 103 | if (domain && domain._disposed) continue; 104 | try { 105 | if (domain) 106 | domain.enter(); 107 | var threw = true; 108 | if (!first._ctx || first._ctx === first) 109 | first._onTimeout(); 110 | else 111 | first._onTimeout.call(first._ctx); 112 | 113 | if (domain) 114 | domain.exit(); 115 | threw = false; 116 | } finally { 117 | if (threw) { 118 | process.nextTick(function() { 119 | list[kOnTimeout](); 120 | }); 121 | } 122 | } 123 | } 124 | } 125 | 126 | debug('%d list empty', msecs); 127 | assert(L.isEmpty(list)); 128 | list.close(); 129 | delete lists[msecs]; 130 | } 131 | 132 | 133 | var unenroll = exports.unenroll = function(item) { 134 | L.remove(item); 135 | 136 | var list = lists[item._idleTimeout]; 137 | // if empty then stop the watcher 138 | debug('unenroll'); 139 | if (list && L.isEmpty(list)) { 140 | debug('unenroll: list empty'); 141 | list.close(); 142 | delete lists[item._idleTimeout]; 143 | } 144 | // if active is called later, then we want to make sure not to insert again 145 | item._idleTimeout = -1; 146 | }; 147 | 148 | 149 | // Does not start the time, just sets up the members needed. 150 | exports.enroll = function(item, msecs) { 151 | // if this item was already in a list somewhere 152 | // then we should unenroll it from that 153 | if (item._idleNext) unenroll(item); 154 | 155 | // Ensure that msecs fits into signed int32 156 | if (msecs > 0x7fffffff) { 157 | msecs = 0x7fffffff; 158 | } 159 | 160 | item._idleTimeout = msecs; 161 | L.init(item); 162 | }; 163 | 164 | 165 | // call this whenever the item is active (not idle) 166 | // it will reset its timeout. 167 | exports.active = function(item) { 168 | var msecs = item._idleTimeout; 169 | if (msecs >= 0) { 170 | 171 | var list = lists[msecs]; 172 | if (!list || L.isEmpty(list)) { 173 | insert(item, msecs); 174 | } else { 175 | item._idleStart = Timer.now(); 176 | L.append(list, item); 177 | } 178 | } 179 | }; 180 | 181 | 182 | /* 183 | * DOM-style timers 184 | */ 185 | 186 | 187 | exports.setTimeout = function(callback, ctx, after) { 188 | var timer; 189 | 190 | after *= 1; // coalesce to number or NaN 191 | 192 | if (!(after >= 1 && after <= TIMEOUT_MAX)) { 193 | after = 1; // schedule on next tick, follows browser behaviour 194 | } 195 | 196 | timer = new Timeout(after); 197 | timer._ctx = ctx; 198 | timer._onTimeout = callback; 199 | 200 | if (process.domain) timer.domain = process.domain; 201 | 202 | exports.active(timer); 203 | 204 | return timer; 205 | }; 206 | 207 | 208 | exports.clearTimeout = function(timer) { 209 | if (timer && (timer[kOnTimeout] || timer._onTimeout)) { 210 | timer[kOnTimeout] = timer._onTimeout = null; 211 | if (timer instanceof Timeout) { 212 | timer.close(); // for after === 0 213 | } else { 214 | exports.unenroll(timer); 215 | } 216 | } 217 | }; 218 | 219 | 220 | exports.setInterval = function(callback, repeat) { 221 | repeat *= 1; // coalesce to number or NaN 222 | 223 | if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { 224 | repeat = 1; // schedule on next tick, follows browser behaviour 225 | } 226 | 227 | var timer = new Timeout(repeat); 228 | var args = Array.prototype.slice.call(arguments, 2); 229 | timer._onTimeout = wrapper; 230 | timer._repeat = true; 231 | 232 | if (process.domain) timer.domain = process.domain; 233 | exports.active(timer); 234 | 235 | return timer; 236 | 237 | function wrapper() { 238 | callback.apply(this, args); 239 | // If callback called clearInterval(). 240 | if (timer._repeat === false) return; 241 | // If timer is unref'd (or was - it's permanently removed from the list.) 242 | if (this._handle) { 243 | this._handle.start(repeat, 0); 244 | } else { 245 | timer._idleTimeout = repeat; 246 | exports.active(timer); 247 | } 248 | } 249 | }; 250 | 251 | 252 | exports.clearInterval = function(timer) { 253 | if (timer && timer._repeat) { 254 | timer._repeat = false; 255 | clearTimeout(timer); 256 | } 257 | }; 258 | 259 | 260 | var Timeout = function(after) { 261 | this._idleTimeout = after; 262 | this._idlePrev = this; 263 | this._idleNext = this; 264 | this._idleStart = null; 265 | this._onTimeout = null; 266 | this._repeat = false; 267 | this._ctx = this; 268 | }; 269 | 270 | Timeout.prototype.unref = function() { 271 | if (!this._handle) { 272 | var now = Timer.now(); 273 | if (!this._idleStart) this._idleStart = now; 274 | var delay = this._idleStart + this._idleTimeout - now; 275 | if (delay < 0) delay = 0; 276 | exports.unenroll(this); 277 | this._handle = new Timer(); 278 | this._handle[kOnTimeout] = this._onTimeout; 279 | this._handle.start(delay, 0); 280 | this._handle.domain = this.domain; 281 | this._handle.unref(); 282 | } else { 283 | this._handle.unref(); 284 | } 285 | }; 286 | 287 | Timeout.prototype.ref = function() { 288 | if (this._handle) 289 | this._handle.ref(); 290 | }; 291 | 292 | Timeout.prototype.close = function() { 293 | this._onTimeout = this._ctx = null; 294 | if (this._handle) { 295 | this._handle[kOnTimeout] = null; 296 | this._handle.close(); 297 | } else { 298 | exports.unenroll(this); 299 | } 300 | }; 301 | 302 | 303 | var immediateQueue = {}; 304 | L.init(immediateQueue); 305 | 306 | 307 | function processImmediate() { 308 | var queue = immediateQueue; 309 | 310 | immediateQueue = {}; 311 | L.init(immediateQueue); 312 | 313 | while (L.isEmpty(queue) === false) { 314 | var immediate = L.shift(queue); 315 | var domain = immediate.domain; 316 | if (domain) domain.enter(); 317 | immediate._onImmediate(); 318 | if (domain) domain.exit(); 319 | } 320 | 321 | // Only round-trip to C++ land if we have to. Calling clearImmediate() on an 322 | // immediate that's in |queue| is okay. Worst case is we make a superfluous 323 | // call to NeedImmediateCallbackSetter(). 324 | if (L.isEmpty(immediateQueue)) { 325 | process._needImmediateCallback = false; 326 | } 327 | } 328 | 329 | 330 | exports.setImmediate = function(callback) { 331 | var immediate = {}, args; 332 | 333 | L.init(immediate); 334 | 335 | immediate._onImmediate = callback; 336 | 337 | if (arguments.length > 1) { 338 | args = Array.prototype.slice.call(arguments, 1); 339 | 340 | immediate._onImmediate = function() { 341 | callback.apply(immediate, args); 342 | }; 343 | } 344 | 345 | if (!process._needImmediateCallback) { 346 | process._needImmediateCallback = true; 347 | process._immediateCallback = processImmediate; 348 | } 349 | 350 | if (process.domain) immediate.domain = process.domain; 351 | 352 | L.append(immediateQueue, immediate); 353 | 354 | return immediate; 355 | }; 356 | 357 | 358 | exports.clearImmediate = function(immediate) { 359 | if (!immediate) return; 360 | 361 | immediate._onImmediate = undefined; 362 | 363 | L.remove(immediate); 364 | 365 | if (L.isEmpty(immediateQueue)) { 366 | process._needImmediateCallback = false; 367 | } 368 | }; 369 | 370 | 371 | // Internal APIs that need timeouts should use timers._unrefActive isntead of 372 | // timers.active as internal timeouts shouldn't hold the loop open 373 | 374 | var unrefList, unrefTimer; 375 | 376 | 377 | function unrefTimeout() { 378 | var now = Timer.now(); 379 | 380 | debug('unrefTimer fired'); 381 | 382 | var first; 383 | while (first = L.peek(unrefList)) { 384 | var diff = now - first._idleStart; 385 | 386 | if (diff < first._idleTimeout) { 387 | diff = first._idleTimeout - diff; 388 | unrefTimer.start(diff, 0); 389 | unrefTimer.when = now + diff; 390 | debug('unrefTimer rescheudling for later'); 391 | return; 392 | } 393 | 394 | L.remove(first); 395 | 396 | var domain = first.domain; 397 | 398 | if (!first._onTimeout) continue; 399 | if (domain && domain._disposed) continue; 400 | 401 | try { 402 | if (domain) domain.enter(); 403 | var threw = true; 404 | debug('unreftimer firing timeout'); 405 | if (!first._ctx || first._ctx === first) 406 | first._onTimeout(); 407 | else 408 | first._onTimeout.call(first._ctx); 409 | threw = false; 410 | if (domain) domain.exit(); 411 | } finally { 412 | if (threw) process.nextTick(unrefTimeout); 413 | } 414 | } 415 | 416 | debug('unrefList is empty'); 417 | unrefTimer.when = -1; 418 | } 419 | 420 | 421 | exports._unrefActive = function(item) { 422 | var msecs = item._idleTimeout; 423 | if (!msecs || msecs < 0) return; 424 | assert(msecs >= 0); 425 | 426 | L.remove(item); 427 | 428 | if (!unrefList) { 429 | debug('unrefList initialized'); 430 | unrefList = {}; 431 | L.init(unrefList); 432 | 433 | debug('unrefTimer initialized'); 434 | unrefTimer = new Timer(); 435 | unrefTimer.unref(); 436 | unrefTimer.when = -1; 437 | unrefTimer[kOnTimeout] = unrefTimeout; 438 | } 439 | 440 | var now = Timer.now(); 441 | item._idleStart = now; 442 | 443 | if (L.isEmpty(unrefList)) { 444 | debug('unrefList empty'); 445 | L.append(unrefList, item); 446 | 447 | unrefTimer.start(msecs, 0); 448 | unrefTimer.when = now + msecs; 449 | debug('unrefTimer scheduled'); 450 | return; 451 | } 452 | 453 | var when = now + msecs; 454 | 455 | debug('unrefList find where we can insert'); 456 | 457 | var cur, them; 458 | 459 | for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) { 460 | them = cur._idleStart + cur._idleTimeout; 461 | 462 | if (when < them) { 463 | debug('unrefList inserting into middle of list'); 464 | 465 | L.append(cur, item); 466 | 467 | if (unrefTimer.when > when) { 468 | debug('unrefTimer is scheduled to fire too late, reschedule'); 469 | unrefTimer.start(msecs, 0); 470 | unrefTimer.when = when; 471 | } 472 | 473 | return; 474 | } 475 | } 476 | 477 | debug('unrefList append to end'); 478 | L.append(unrefList, item); 479 | }; 480 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-compare", 3 | "version": "0.1.0", 4 | "description": "Compare the performance and code of multiple async patterns", 5 | "main": "perf.js", 6 | "dependencies": { 7 | "async": "^0.9.0", 8 | "bluebird": "^2.3.0", 9 | "co": "^3.1.0", 10 | "deferred-queue": "^0.5.1", 11 | "esprima": "~1.0.3", 12 | "genny": "~0.5.0", 13 | "gens": "^1.1.0", 14 | "kew": "^0.4.0", 15 | "long-stack-traces": "~0.1.2", 16 | "optimist": "~0.6.0", 17 | "p-promise": "~0.4.0", 18 | "q": "^1.0.1", 19 | "rsvp": "^3.0.13", 20 | "rx": "^2.3.4", 21 | "source-map-support": "~0.2.3", 22 | "stratifiedjs": "~0.14.0", 23 | "streamline": "~0.6.0", 24 | "suspend": "~0.3.0", 25 | "text-table": "~0.1.1", 26 | "traceur": "0.0.4", 27 | "when": "^3.4.4" 28 | }, 29 | "devDependencies": {}, 30 | "scripts": { 31 | "test": "echo \"Error: no test specified\" && exit 1" 32 | }, 33 | "keywords": [ 34 | "generators", 35 | "fibers", 36 | "promises", 37 | "callbacks", 38 | "comparison", 39 | "compare", 40 | "async" 41 | ], 42 | "author": "spion", 43 | "license": "MIT" 44 | } 45 | -------------------------------------------------------------------------------- /performance.js: -------------------------------------------------------------------------------- 1 | 2 | var args = require('optimist').argv; 3 | 4 | var util = require('util'); 5 | 6 | var path = require('path'); 7 | 8 | function printPlatform() { 9 | console.log("\nPlatform info:"); 10 | var os = require("os"); 11 | var v8 = process.versions.v8; 12 | var node = process.versions.node; 13 | var plat = os.type() + " " + os.release() + " " + os.arch() + "\nNode.JS " + node + "\nV8 " + v8; 14 | var cpus = os.cpus().map(function(cpu){ 15 | return cpu.model; 16 | }).reduce(function(o, model){ 17 | if( !o[model] ) o[model] = 0; 18 | o[model]++; 19 | return o; 20 | }, {}); 21 | cpus = Object.keys(cpus).map(function( key ){ 22 | return key + " \u00d7 " + cpus[key]; 23 | }).join("\n"); 24 | console.log(plat + "\n" + cpus + "\n"); 25 | } 26 | 27 | var perf = module.exports = function(args, done) { 28 | 29 | var errs = 0; 30 | var lastErr; 31 | var times = args.n; 32 | 33 | global.asyncTime = args.t; 34 | 35 | if (args.longStackSupport) { 36 | global.longStackSupport = require('q').longStackSupport 37 | = args.longStackSupport; 38 | require('bluebird').longStackTraces(); 39 | } 40 | 41 | var fn = require(args.file); 42 | 43 | var start = Date.now(); 44 | 45 | 46 | var warmedUp = 0; 47 | var tot = Math.min( 350, times ); 48 | for (var k = 0, kn = tot; k < kn; ++k) 49 | fn(k,'b','c', warmup); 50 | 51 | var memMax; var memStart; var start; 52 | function warmup() { 53 | warmedUp++ 54 | if( warmedUp === tot ) { 55 | start = Date.now(); 56 | 57 | memStart = process.memoryUsage().rss; 58 | for (var k = 0, kn = args.n; k < kn; ++k) 59 | fn(k, 'b', 'c', cb); 60 | memMax = process.memoryUsage().rss; 61 | } 62 | } 63 | 64 | function cb (err) { 65 | if (err) { 66 | ++errs; 67 | lastErr = err; 68 | } 69 | memMax = Math.max(memMax, process.memoryUsage().rss); 70 | if (!--times) { 71 | fn.end && fn.end(); 72 | done(null, { 73 | time: Date.now() - start, 74 | mem: (memMax - memStart)/1024/1024, 75 | errors: errs, 76 | lastErr: lastErr ? lastErr.stack : null 77 | }); 78 | } 79 | } 80 | } 81 | 82 | 83 | function report(err, res) { 84 | console.log(JSON.stringify(res)); 85 | } 86 | 87 | if (args.file) { 88 | perf(args, function(err, res) { 89 | report(err, res); 90 | if (res.lastErr) 91 | console.error(res.lastErr); 92 | }); 93 | } else { 94 | var cp = require('child_process') 95 | var async = require('async'); 96 | var fs = require('fs'); 97 | var dir = __dirname + '/examples'; 98 | 99 | var table = require('text-table'); 100 | 101 | 102 | var files = args._.filter(function(f) { 103 | return !/^src-/.test(path.basename(f)); 104 | }); 105 | 106 | if (args.n) 107 | measure(files, args.n, args.t, function(err, res) { 108 | console.log(""); 109 | console.log("results for", args.n, "parallel executions,", 110 | args.t, "ms per I/O op"); 111 | 112 | res.sort(function(r1, r2) { 113 | return parseFloat(r1.data.time) - parseFloat(r2.data.time) 114 | }); 115 | console.log(""); 116 | res = res.map(function(r) { 117 | var failText = 'N/A'; 118 | if (r.data.timeout) failText = 'T/O'; 119 | return [path.basename(r.file), 120 | r.data.mem != null ? r.data.time: failText, 121 | r.data.mem != null ? r.data.mem.toFixed(2) : failText] 122 | }); 123 | 124 | res = [['file', 'time(ms)', 'memory(MB)']].concat(res) 125 | 126 | console.log(table(res, {align: ['l', 'r', 'r']})); 127 | printPlatform(); 128 | 129 | }); 130 | else { 131 | var measurements = (args.ns || '100,500,1000,1500,2000').split(','); 132 | async.mapSeries(measurements, function(n, done) { 133 | console.log("--- n =", n, "---"); 134 | measure(files, n, args.t != null ? args.t : n * args.dt, function(err, res) { 135 | return done(null, {n: n, res: res}); 136 | }); 137 | }, function(err, all) { 138 | //structure: 139 | //[{n: n, res: [{ file: file, data: {time: time, mem: mem}}]}] 140 | var times = [], mems = []; 141 | for (var k = 0; k < all[0].res.length; ++k) { 142 | var file = all[0].res[k].file; 143 | // skip missing 144 | if (all[0].res[k].data.missing) 145 | continue; 146 | var memf = {label: path.basename(file), data: []}; 147 | var timef = {label: path.basename(file), data: []}; 148 | for (var n = 0; n < all.length; ++n) { 149 | var requests = all[n].n, 150 | time = all[n].res[k].data.time, 151 | mem = all[n].res[k].data.mem; 152 | timef.data.push([requests, time]); 153 | memf.data.push( [requests, mem]); 154 | } 155 | times.push(timef); 156 | mems.push(memf); 157 | } 158 | console.log("--------- time ---------"); 159 | console.log(util.inspect(times, false, 10)) 160 | console.log("--------- mem ----------"); 161 | console.log(util.inspect(mems, false, 10)) 162 | }) 163 | } 164 | } 165 | 166 | 167 | function measure(files, requests, time, callback) { 168 | async.mapSeries(files, function(f, done) { 169 | console.log("benchmarking", f); 170 | 171 | var argsFork = [__filename, 172 | '--n', requests, 173 | '--t', time, 174 | '--file', f]; 175 | if (args.harmony) argsFork.unshift('--harmony'); 176 | if (args.longStackSupport) argsFork.push('--longStackSupport'); 177 | 178 | var p = cp.spawn(process.execPath, argsFork); 179 | 180 | var complete = false, timedout = false; 181 | if (args.timeout) setTimeout(function() { 182 | if (complete) return; 183 | timedout = true; 184 | p.kill(); 185 | }, args.timeout); 186 | 187 | var r = { file: f, data: [] }; 188 | p.stdout.on('data', function(d) { r.data.push(d.toString()); }); 189 | p.stdout.pipe(process.stdout); 190 | p.stdout.on('end', function(code) { 191 | complete = true; 192 | try { 193 | r.data = JSON.parse(r.data.join('')); 194 | } catch(e) { 195 | r.data = {time: Number.POSITIVE_INFINITY, mem: null, 196 | missing: true, timeout: timedout}; 197 | } 198 | done(null, r); 199 | }); 200 | }, callback); 201 | } 202 | --------------------------------------------------------------------------------