Last active
November 5, 2015 06:26
-
-
Save elado/c493a56925bfdeadc429 to your computer and use it in GitHub Desktop.
reusePromise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const pendingPromisesMap = () => { | |
const FN_INDEX = 0 | |
const KEY_INDEX = 1 | |
const VALUE_INDEX = 2 | |
const a = [] | |
function is(item, fn, key) { | |
if (item[FN_INDEX] == fn && item[KEY_INDEX] == key) { | |
return true | |
} | |
return false | |
} | |
function getIndex(fn, key) { | |
for (let i = 0, l = a.length; i < l; i++) { | |
const item = a[i] | |
if (is(item, fn, key)) { | |
return i | |
} | |
} | |
return -1 | |
} | |
function get(fn, key) { | |
const i = getIndex(fn, key) | |
if (a[i]) { | |
return a[i][VALUE_INDEX] | |
} | |
return null | |
} | |
function set(fn, key, value) { | |
const i = getIndex(fn, key) | |
if (i > -1) { | |
a[i][VALUE_INDEX] = value | |
} | |
else { | |
const obj = [] | |
obj[FN_INDEX] = fn | |
obj[KEY_INDEX] = key | |
obj[VALUE_INDEX] = value | |
a.push(obj) | |
} | |
return value | |
} | |
function del(fn, key) { | |
const i = getIndex(fn, key) | |
if (i > -1) { | |
a.splice(i, 1) | |
} | |
} | |
function clear() { | |
a.length = 0 | |
} | |
return { | |
get, | |
set, | |
del, | |
clear | |
} | |
}() | |
export default function reusePromise(target, name, descriptor) { | |
let getter = descriptor.value | |
descriptor.value = function () { | |
const key = JSON.stringify(arguments) | |
const pendingPromise = pendingPromisesMap.get(getter, key) | |
if (pendingPromise) { | |
return pendingPromise | |
} | |
const forgetPromise = () => { | |
pendingPromisesMap.del(getter, key) | |
} | |
let promise = getter.apply(this, arguments).then((value) => { | |
forgetPromise() | |
return value | |
}, (err) => { | |
forgetPromise() | |
throw err | |
}) | |
pendingPromisesMap.set(getter, key, promise) | |
return promise | |
} | |
return descriptor | |
} | |
reusePromise.clear = pendingPromisesMap.clear | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import assert from 'assert' | |
import reusePromise from './reusePromise' | |
describe('reusePromise', function () { | |
class Test { | |
@reusePromise | |
find(id) { | |
return new Promise((fulfil, reject) => { | |
setTimeout(() => { | |
fulfil({ id: id }) | |
}, 5) | |
}) | |
} | |
@reusePromise | |
findWithError(id) { | |
return new Promise((fulfil, reject) => { | |
setTimeout(() => { | |
reject(new Error) | |
}, 5) | |
}) | |
} | |
} | |
let test | |
beforeEach(function () { | |
test = new Test() | |
}) | |
it('reuses the same promise if the current one is still pending', function () { | |
const p1 = test.find(1) | |
assert.equal(p1, test.find(1)) | |
const p2 = test.find(2) | |
assert.equal(p2, test.find(2)) | |
assert.notEqual(p1, test.find(2)) | |
}) | |
it('asks for a new promise if previous one was fulfilled', function (done) { | |
const p1 = test.find(1) | |
assert.equal(p1, test.find(1)) | |
setTimeout(() => { | |
assert.equal(p1, test.find(1)) | |
setTimeout(() => { | |
assert.notEqual(p1, test.find(1)) | |
done() | |
}, 7) | |
}, 2) | |
}) | |
it('fullfils the promise once with same value', function (done) { | |
const promises = [test.find(1), test.find(1), test.find(1)] | |
const values = [] | |
let pending = promises.length | |
promises.forEach((p) => { | |
p.then((v) => { | |
values.push(v) | |
check() | |
}) | |
}) | |
function check() { | |
pending-- | |
if (pending == 0) { | |
for (let i = 0; i < values.length - 1; i++) { | |
assert.equal(values[i], values[i + 1]) | |
} | |
done() | |
} | |
} | |
}) | |
it('rejects the promise once with same error', function (done) { | |
const promises = [test.findWithError(1), test.findWithError(1), test.findWithError(1)] | |
const errors = [] | |
let pending = promises.length | |
promises.forEach((p) => { | |
p.then(() => { | |
assert.fail() | |
}, (err) => { | |
errors.push(err) | |
check() | |
}) | |
}) | |
function check() { | |
pending-- | |
if (pending == 0) { | |
for (let i = 0; i < errors.length - 1; i++) { | |
assert.equal(errors[i], errors[i + 1]) | |
} | |
done() | |
} | |
} | |
}) | |
}) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment