const regExpSync = (str, pat, fn) => {
let loc = 0;
let res;
while((res = pat.exec(str.substring(loc)))){
const rtn = fn(res);
if(rtn) return rtn;
loc += res.length + res.index;
}
};
const regExpAsync = async (str, pat, fn) => {
let loc = 0;
let res;
while((res = pat.exec(str.substring(loc)))){
const rtn = await fn(res);
if(rtn) return rtn;
loc += res.length + res.index;
}
};
const testPat = new RegExp('[a-z]+');
const testStr = '------aa-----abc--------zz--------dd----------';
console.log('-');
regExpSync(testStr, testPat, ([str]) => { console.log(str); return false; });
console.log('-');
await regExpAsync(testStr, testPat, async ([str]) => { console.log(str); return false; });
console.log('-');
I have almost identical async and synchronous code.
This is just an example, in reality it is more complex logic that has nothing to do with regular expressions.
Anyway, both work as callback function fn.
I am modifying both whenever there is a change in that logic.
Any ideas on minimizing that modification?
I tried the following but it’s not very convenient.
Can we increase code reuse even just a little bit more here?
const regExp = (str, pat, fn) => {
if(fn.constructor.name == 'AsyncFunction') return (async () => {
let loc = 0;
let res;
while((res = pat.exec(str.substring(loc)))){
const rtn = await fn(res);
if(rtn) return rtn;
loc += res.length + res.index;
}
})();
else {
let loc = 0;
let res;
while((res = pat.exec(str.substring(loc)))){
const rtn = fn(res);
if(rtn) return rtn;
loc += res.length + res.index;
}
}
};
2
Answers
You can improve the code reuse by separating the common logic into a helper function and then utilizing that helper function in both the synchronous and asynchronous versions.
Link In the above code, you can extract the common matching logic into the regExpMatch function, which takes the input string, the pattern, and the callback function fn. This function is then used by both regExpSync and regExpAsync.
You can use a generator function to abstract out the looping:
Notice that with the iterator returned by
regExp
, you can even simplify this more by using iterator helper methods:However, if that doesn’t fit your bill, you can actually use a generator function for the whole logic with
yield
where a promise might need to beawait
ed, then run it either synchronously (just skipping the yielded value and passing it back in immediately) or asynchronously (by waiting for the yielded promise and passing/throwing its result back in):