├── .gitignore ├── .travis.yml ├── example.js ├── package.json ├── index.js ├── LICENSE ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.12' 5 | - '4.0' 6 | - '5.0' 7 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var lastOneWins = require('./') 2 | 3 | var print = lastOneWins(function (val, cb) { 4 | setTimeout(function () { 5 | console.log(val) 6 | cb() 7 | }, 100) 8 | }) 9 | 10 | print('hello') 11 | print(' dank ') 12 | print(' dank ') 13 | print(' dank ') 14 | print(' dank ') 15 | print(' dank ') 16 | print(' dank ') 17 | print('world') 18 | 19 | setTimeout(function () { 20 | print('hmm') 21 | print('lol') 22 | print('lol') 23 | print('lol') 24 | print('now?') 25 | }, 300) 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "last-one-wins", 3 | "version": "1.0.4", 4 | "description": "Make sure the last sync call to an async function is executed after all previous ones have finished", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "standard": "^5.4.1", 9 | "tape": "^4.2.2" 10 | }, 11 | "scripts": { 12 | "test": "standard && tape test.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/mafintosh/last-one-wins.git" 17 | }, 18 | "author": "Mathias Buus (@mafintosh)", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/mafintosh/last-one-wins/issues" 22 | }, 23 | "homepage": "https://github.com/mafintosh/last-one-wins" 24 | } 25 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (work) { 2 | var pending = null 3 | var callback = null 4 | var callbacks = null 5 | var next = null 6 | 7 | return function (val, cb) { 8 | next = val 9 | update(cb || noop) 10 | } 11 | 12 | function update (cb) { 13 | if (callback) { 14 | if (!pending) pending = [] 15 | pending.push(cb) 16 | return 17 | } 18 | 19 | var val = next 20 | next = null 21 | callback = cb 22 | work(val, done) 23 | } 24 | 25 | function done (err) { 26 | var cb = callback 27 | var cbs = callbacks 28 | callbacks = null 29 | callback = null 30 | 31 | if (pending) { 32 | callbacks = pending 33 | pending = null 34 | update(noop) 35 | } 36 | 37 | if (cbs) { 38 | for (var i = 0; i < cbs.length; i++) cbs[i](err) 39 | } 40 | cb(err) 41 | } 42 | } 43 | 44 | function noop (_) {} 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mathias Buus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # last-one-wins 2 | 3 | Make sure the last sync call to an async function is executed after all previous ones have finished 4 | 5 | ``` 6 | npm install last-one-wins 7 | ``` 8 | 9 | [![build status](http://img.shields.io/travis/mafintosh/last-one-wins.svg?style=flat)](http://travis-ci.org/mafintosh/last-one-wins) 10 | 11 | ## Usage 12 | 13 | ``` js 14 | var low = require('last-one-wins') 15 | 16 | var pick = low(function (num, cb) { 17 | setTimeout(function () { 18 | console.log('picked', num) 19 | cb() 20 | }, Math.floor(Math.random() * 100)) 21 | }) 22 | 23 | pick(1) 24 | pick(2) 25 | pick(3) 26 | pick(4) 27 | pick(5) // this one will always win since its called the last 28 | ``` 29 | 30 | Calling the above will print out 31 | 32 | ``` 33 | picked 1 34 | picked 5 35 | ``` 36 | 37 | ## API 38 | 39 | #### `var fn = low(asyncWorker)` 40 | 41 | Wrap a async function to make sure that the last sync call to that 42 | function is executed after any previous calls. Note that not all calls 43 | are executed - only the last one is guaranteed to be executed. 44 | 45 | `fn` and `asyncWorker` should have the signature `(value, cb)` 46 | 47 | ## Use with leveldb 48 | 49 | This module is useful if you want sync a value to a leveldb and want to 50 | make sure the latest version of that value is the one written. For example 51 | 52 | ``` js 53 | var update = low(function (val, cb) { 54 | db.put('my-key', val, cb) 55 | }) 56 | 57 | update('a') 58 | update('b') 59 | update('c') 60 | ``` 61 | 62 | Is guaranteed to always write `c` to the key `my-key`. 63 | 64 | ## License 65 | 66 | MIT 67 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var low = require('./') 2 | var tape = require('tape') 3 | 4 | tape('last one wins', function (t) { 5 | t.plan(2) 6 | 7 | var expected = [1, 5] 8 | var fn = low(function (num, cb) { 9 | t.same(num, expected.shift()) 10 | process.nextTick(cb) 11 | }) 12 | 13 | fn(1) 14 | fn(2) 15 | fn(3) 16 | fn(4) 17 | fn(5) 18 | }) 19 | 20 | tape('calls callbacks', function (t) { 21 | t.plan(7) 22 | 23 | var expected = [1, 5] 24 | var fn = low(function (num, cb) { 25 | t.same(num, expected.shift()) 26 | process.nextTick(cb) 27 | }) 28 | 29 | fn(1, called) 30 | fn(2, called) 31 | fn(3, called) 32 | fn(4, called) 33 | fn(5, called) 34 | 35 | function called () { 36 | t.ok(true) 37 | } 38 | }) 39 | 40 | tape('calls callbacks twice', function (t) { 41 | t.plan(13) 42 | 43 | var expected = [1, 5, 6, 9] 44 | var fn = low(function (num, cb) { 45 | t.same(num, expected.shift()) 46 | process.nextTick(cb) 47 | }) 48 | 49 | fn(1, called) 50 | fn(2, called) 51 | fn(3, called) 52 | fn(4, called) 53 | fn(5, function () { 54 | called() 55 | fn(6, called) 56 | fn(7, called) 57 | fn(8, called) 58 | fn(9, called) 59 | }) 60 | 61 | function called () { 62 | t.ok(true) 63 | } 64 | }) 65 | 66 | tape('calls callbacks with errors', function (t) { 67 | t.plan(7) 68 | 69 | var expected = [1, 5] 70 | var fn = low(function (num, cb) { 71 | t.same(num, expected.shift()) 72 | process.nextTick(function () { 73 | if (!expected.length) cb(new Error('lol')) 74 | else cb() 75 | }) 76 | }) 77 | 78 | fn(1, called) 79 | fn(2, calledWithError) 80 | fn(3, calledWithError) 81 | fn(4, calledWithError) 82 | fn(5, calledWithError) 83 | 84 | function calledWithError (err) { 85 | t.same(err.message, 'lol') 86 | } 87 | 88 | function called (err) { 89 | t.ok(!err) 90 | } 91 | }) 92 | 93 | tape('works with falsy arg', function (t) { 94 | t.plan(7) 95 | 96 | var expected = [1, 0] 97 | var fn = low(function (num, cb) { 98 | t.same(num, expected.shift()) 99 | process.nextTick(cb) 100 | }) 101 | 102 | fn(1, called) 103 | fn(2, called) 104 | fn(3, called) 105 | fn(4, called) 106 | fn(0, called) 107 | 108 | function called () { 109 | t.ok(true) 110 | } 111 | }) 112 | --------------------------------------------------------------------------------