Performs left-to-right function composition for asynchronous functions.
Use Array.prototype.reduce() with the spread operator (...) to perform left-to-right function composition using Promise.then(). The functions can return a combination of: simple values, Promise's, or they can be defined as async ones returning through await. All functions must be unary.
const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg)); -
const sum = pipeAsyncFunctions( +
+const sum = pipeAsyncFunctions( x => x + 1, x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)), x => x + 3, diff --git a/docs/function.html b/docs/function.html index a50a88551..0de830c9d 100644 --- a/docs/function.html +++ b/docs/function.html @@ -121,7 +121,11 @@ console.log< console.log(freddyBound('hi', '!')); // 'hi fred!'
Chains asynchronous functions.
Loop through an array of functions containing asynchronous events, calling next when each asynchronous event has completed.
const chainAsync = fns => { let curr = 0; - const next = () => fns[curr++](next); + const last = fns[fns.length - 1]; + const next = () => { + const fn = fns[curr++]; + fn === last ? fn() : fn(next); + }; next(); };
chainAsync([ @@ -130,7 +134,11 @@ console.log< setTimeout(next, 1000); }, next => { - console.log('1 second'); + console.log('1 second'); + setTimeout(next, 1000); + }, + () => { + console.log('2 second'); } ]);
Performs right-to-left function composition.
Use Array.prototype.reduce() to perform right-to-left function composition. The last (rightmost) function can accept one or more arguments; the remaining functions must be unary.
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); diff --git a/docs/index.html b/docs/index.html index 54e8f4df0..7137a28c8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -405,7 +405,8 @@
Takes a predicate and array, like Array.prototype.filter(), but only keeps x if pred(x) === false.
const reject = (pred, array) => array.filter((...args) => !pred(...args));
reject(x => x % 2 === 0, [1, 2, 3, 4, 5]); // [1, 3, 5] reject(word => word.length > 4, ['Apple', 'Pear', 'Kiwi', 'Banana']); // ['Pear', 'Kiwi'] -
Removes elements from an array for which the given function returns false.
Use Array.prototype.filter() to find array elements that return truthy values and Array.prototype.reduce() to remove elements using Array.prototype.splice(). The func is invoked with three arguments (value, index, array).
const remove = (arr, func) => +
Removes elements from an array for which the given function returns false.
Use Array.prototype.filter() to find array elements that return truthy values and Array.prototype.reduce() to remove elements using Array.prototype.splice(). The func is invoked with three arguments (value, index, array).
+const remove = (arr, func) => Array.isArray(arr) ? arr.filter(func).reduce((acc, val) => { arr.splice(arr.indexOf(val), 1); diff --git a/snippet_data/snippetList.json b/snippet_data/snippetList.json index 46f39c443..f02ddbf4a 100644 --- a/snippet_data/snippetList.json +++ b/snippet_data/snippetList.json @@ -370,7 +370,7 @@ "archived": false }, "meta": { - "hash": "8c245a7fc94edffaf5cae4c28f37ed2e989772a9663a5f5bce98147f708a712e" + "hash": "ae8f373cb6c661896ce4256e9f8a2f7f9c14f1699651d366af038f66a938f70e" } }, { @@ -3348,7 +3348,7 @@ "archived": false }, "meta": { - "hash": "4815876fd6dbb17ad34c0d8918e7a72d837104f9beee7dc51b0fa73057b9e83e" + "hash": "0b04f5fe668888db0dc360535cd999669258019f0682eb6e4ad3a1164e408d13" } }, { @@ -3720,7 +3720,7 @@ "archived": false }, "meta": { - "hash": "ec9cb9384817f84cf0bacd62a23b69b2304fa2cf0352b16d3950b21d48c04f11" + "hash": "536833a64ce0c000b82327ed1bb9bcb82e35b237f75aefcca0e0d2def4e9cb63" } }, { diff --git a/snippet_data/snippets.json b/snippet_data/snippets.json index 11c22ba95..a56cbd230 100644 --- a/snippet_data/snippets.json +++ b/snippet_data/snippets.json @@ -534,9 +534,9 @@ "fileName": "chainAsync.md", "text": "Chains asynchronous functions.\n\nLoop through an array of functions containing asynchronous events, calling `next` when each asynchronous event has completed.", "codeBlocks": { - "es6": "const chainAsync = fns => {\n let curr = 0;\n const next = () => fns[curr++](next);\n next();\n};", - "es5": "var chainAsync = function chainAsync(fns) {\n var curr = 0;\n\n var next = function next() {\n return fns[curr++](next);\n };\n\n next();\n};", - "example": "chainAsync([\n next => {\n console.log('0 seconds');\n setTimeout(next, 1000);\n },\n next => {\n console.log('1 second');\n }\n]);" + "es6": "const chainAsync = fns => {\n let curr = 0;\n const last = fns[fns.length - 1];\n const next = () => {\n const fn = fns[curr++];\n fn === last ? fn() : fn(next);\n };\n next();\n};", + "es5": "var chainAsync = function chainAsync(fns) {\n var curr = 0;\n var last = fns[fns.length - 1];\n\n var next = function next() {\n var fn = fns[curr++];\n fn === last ? fn() : fn(next);\n };\n\n next();\n};", + "example": "chainAsync([\n next => {\n console.log('0 seconds');\n setTimeout(next, 1000);\n },\n next => {\n console.log('1 second');\n setTimeout(next, 1000);\n },\n () => {\n console.log('2 second');\n }\n]);" }, "tags": [ "function", @@ -545,7 +545,7 @@ }, "meta": { "archived": false, - "hash": "8c245a7fc94edffaf5cae4c28f37ed2e989772a9663a5f5bce98147f708a712e" + "hash": "ae8f373cb6c661896ce4256e9f8a2f7f9c14f1699651d366af038f66a938f70e" } }, { @@ -4930,7 +4930,7 @@ }, "meta": { "archived": false, - "hash": "4815876fd6dbb17ad34c0d8918e7a72d837104f9beee7dc51b0fa73057b9e83e" + "hash": "0b04f5fe668888db0dc360535cd999669258019f0682eb6e4ad3a1164e408d13" } }, { @@ -5477,7 +5477,7 @@ }, "meta": { "archived": false, - "hash": "ec9cb9384817f84cf0bacd62a23b69b2304fa2cf0352b16d3950b21d48c04f11" + "hash": "536833a64ce0c000b82327ed1bb9bcb82e35b237f75aefcca0e0d2def4e9cb63" } }, { diff --git a/snippets/chainAsync.md b/snippets/chainAsync.md index 10bbf6613..ef0b2d851 100644 --- a/snippets/chainAsync.md +++ b/snippets/chainAsync.md @@ -7,7 +7,11 @@ Loop through an array of functions containing asynchronous events, calling `next ```js const chainAsync = fns => { let curr = 0; - const next = () => fns[curr++](next); + const last = fns[fns.length - 1]; + const next = () => { + const fn = fns[curr++]; + fn === last ? fn() : fn(next); + }; next(); }; ``` @@ -20,6 +24,10 @@ chainAsync([ }, next => { console.log('1 second'); + setTimeout(next, 1000); + }, + () => { + console.log('2 second'); } ]); ``` diff --git a/snippets/pipeAsyncFunctions.md b/snippets/pipeAsyncFunctions.md index 47d6f620c..61b850a39 100644 --- a/snippets/pipeAsyncFunctions.md +++ b/snippets/pipeAsyncFunctions.md @@ -11,6 +11,7 @@ const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Pr ``` ```js + const sum = pipeAsyncFunctions( x => x + 1, x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)), diff --git a/snippets/remove.md b/snippets/remove.md index 58de9c2e0..1c457d2d9 100644 --- a/snippets/remove.md +++ b/snippets/remove.md @@ -6,6 +6,7 @@ Use `Array.prototype.filter()` to find array elements that return truthy values The `func` is invoked with three arguments (`value, index, array`). ```js + const remove = (arr, func) => Array.isArray(arr) ? arr.filter(func).reduce((acc, val) => { diff --git a/test/_30s.js b/test/_30s.js index 90fcd8c9a..6f60394a8 100644 --- a/test/_30s.js +++ b/test/_30s.js @@ -1868,4 +1868,5 @@ const speechSynthesis = message => { const squareSum = (...args) => args.reduce((squareSum, number) => squareSum + Math.pow(number, 2), 0); -module.exports = {all,allEqual,any,approximatelyEqual,arrayToCSV,arrayToHtmlList,ary,atob,attempt,average,averageBy,bifurcate,bifurcateBy,bind,bindAll,bindKey,binomialCoefficient,bottomVisible,btoa,byteSize,call,capitalize,capitalizeEveryWord,castArray,chainAsync,chunk,clampNumber,cloneRegExp,coalesce,coalesceFactory,collectInto,colorize,compact,compactWhitespace,compose,composeRight,converge,copyToClipboard,countBy,counter,countOccurrences,createElement,createEventHub,CSVToArray,CSVToJSON,currentURL,curry,dayOfYear,debounce,decapitalize,deepClone,deepFlatten,deepFreeze,deepMapKeys,defaults,defer,degreesToRads,delay,detectDeviceType,difference,differenceBy,differenceWith,dig,digitize,distance,drop,dropRight,dropRightWhile,dropWhile,elementContains,elementIsVisibleInViewport,elo,equals,escapeHTML,escapeRegExp,everyNth,extendHex,factorial,fibonacci,filterFalsy,filterNonUnique,filterNonUniqueBy,findKey,findLast,findLastIndex,findLastKey,flatten,flattenObject,flip,forEachRight,formatDuration,forOwn,forOwnRight,fromCamelCase,functionName,functions,gcd,geometricProgression,get,getColonTimeFromDate,getDaysDiffBetweenDates,getImages,getMeridiemSuffixOfInteger,getScrollPosition,getStyle,getType,getURLParameters,groupBy,hammingDistance,hasClass,hasFlags,hashBrowser,hashNode,head,hexToRGB,hide,httpGet,httpPost,httpsRedirect,hz,indentString,indexOfAll,initial,initialize2DArray,initializeArrayWithRange,initializeArrayWithRangeRight,initializeArrayWithValues,initializeNDArray,inRange,insertAfter,insertBefore,intersection,intersectionBy,intersectionWith,invertKeyValues,is,isAbsoluteURL,isAfterDate,isAnagram,isArrayLike,isBeforeDate,isBoolean,isBrowser,isBrowserTabFocused,isDivisible,isDuplexStream,isEmpty,isEven,isFunction,isLowerCase,isNegativeZero,isNil,isNull,isNumber,isObject,isObjectLike,isPlainObject,isPrime,isPrimitive,isPromiseLike,isReadableStream,isSameDate,isSorted,isStream,isString,isSymbol,isTravisCI,isUndefined,isUpperCase,isValidJSON,isWritableStream,join,JSONtoCSV,JSONToFile,last,lcm,longestItem,lowercaseKeys,luhnCheck,mapKeys,mapObject,mapString,mapValues,mask,matches,matchesWith,maxBy,maxDate,maxN,median,memoize,merge,midpoint,minBy,minDate,minN,mostPerformant,negate,nest,nodeListToArray,none,nthArg,nthElement,objectFromPairs,objectToPairs,observeMutations,off,offset,omit,omitBy,on,once,onUserInputChange,orderBy,over,overArgs,pad,palindrome,parseCookie,partial,partialRight,partition,percentile,permutations,pick,pickBy,pipeAsyncFunctions,pipeFunctions,pluralize,powerset,prefix,prettyBytes,primes,promisify,pull,pullAtIndex,pullAtValue,pullBy,radsToDegrees,randomHexColorCode,randomIntArrayInRange,randomIntegerInRange,randomNumberInRange,readFileLines,rearg,recordAnimationFrames,redirect,reducedFilter,reduceSuccessive,reduceWhich,reject,remove,removeNonASCII,renameKeys,reverseString,RGBToHex,round,runAsync,runPromisesInSeries,sample,sampleSize,scrollToTop,sdbm,serializeCookie,setStyle,shallowClone,shank,show,shuffle,similarity,size,sleep,smoothScroll,sortCharactersInString,sortedIndex,sortedIndexBy,sortedLastIndex,sortedLastIndexBy,splitLines,spreadOver,stableSort,standardDeviation,stringPermutations,stripHTMLTags,sum,sumBy,sumPower,symmetricDifference,symmetricDifferenceBy,symmetricDifferenceWith,tail,take,takeRight,takeRightWhile,takeWhile,throttle,times,timeTaken,toCamelCase,toCurrency,toDecimalMark,toggleClass,toHash,toKebabCase,tomorrow,toOrdinalSuffix,toSafeInteger,toSnakeCase,toTitleCase,transform,triggerEvent,truncateString,truthCheckCollection,unary,uncurry,unescapeHTML,unflattenObject,unfold,union,unionBy,unionWith,uniqueElements,uniqueElementsBy,uniqueElementsByRight,uniqueSymmetricDifference,untildify,unzip,unzipWith,URLJoin,UUIDGeneratorBrowser,UUIDGeneratorNode,validateNumber,when,without,words,xProd,yesNo,zip,zipObject,zipWith,binarySearch,celsiusToFahrenheit,cleanObj,collatz,countVowels,factors,fahrenheitToCelsius,fibonacciCountUntilNum,fibonacciUntilNum,heronArea,howManyTimes,httpDelete,httpPut,isArmstrongNumber,isSimilar,JSONToDate,kmphToMph,levenshteinDistance,mphToKmph,pipeLog,quickSort,removeVowels,solveRPN,speechSynthesis,squareSum} \ No newline at end of file +module.exports = {all,allEqual,any,approximatelyEqual,arrayToCSV,arrayToHtmlList,ary,atob,attempt,average,averageBy,bifurcate,bifurcateBy,bind,bindAll,bindKey,binomialCoefficient,bottomVisible,btoa,byteSize,call,capitalize,capitalizeEveryWord,castArray,chainAsync,chunk,clampNumber,cloneRegExp,coalesce,coalesceFactory,collectInto,colorize,compact,compactWhitespace,compose,composeRight,converge,copyToClipboard,countBy,counter,countOccurrences,createElement,createEventHub,CSVToArray,CSVToJSON,currentURL,curry,dayOfYear,debounce,decapitalize,deepClone,deepFlatten,deepFreeze,deepMapKeys,defaults,defer,degreesToRads,delay,detectDeviceType,difference,differenceBy,differenceWith,dig,digitize,distance,drop,dropRight,dropRightWhile,dropWhile,elementContains,elementIsVisibleInViewport,elo,equals,escapeHTML,escapeRegExp,everyNth,extendHex,factorial,fibonacci,filterFalsy,filterNonUnique,filterNonUniqueBy,findKey,findLast,findLastIndex,findLastKey,flatten,flattenObject,flip,forEachRight,formatDuration,forOwn,forOwnRight,fromCamelCase,functionName,functions,gcd,geometricProgression,get,getColonTimeFromDate,getDaysDiffBetweenDates,getImages,getMeridiemSuffixOfInteger,getScrollPosition,getStyle,getType,getURLParameters,groupBy,hammingDistance,hasClass,hasFlags,hashBrowser,hashNode,head,hexToRGB,hide,httpGet,httpPost,httpsRedirect,hz,indentString,indexOfAll,initial,initialize2DArray,initializeArrayWithRange,initializeArrayWithRangeRight,initializeArrayWithValues,initializeNDArray,inRange,insertAfter,insertBefore,intersection,intersectionBy,intersectionWith,invertKeyValues,is,isAbsoluteURL,isAfterDate,isAnagram,isArrayLike,isBeforeDate,isBoolean,isBrowser,isBrowserTabFocused,isDivisible,isDuplexStream,isEmpty,isEven,isFunction,isLowerCase,isNegativeZero,isNil,isNull,isNumber,isObject,isObjectLike,isPlainObject,isPrime,isPrimitive,isPromiseLike,isReadableStream,isSameDate,isSorted,isStream,isString,isSymbol,isTravisCI,isUndefined,isUpperCase,isValidJSON,isWritableStream,join,JSONtoCSV,JSONToFile,last,lcm,longestItem,lowercaseKeys,luhnCheck,mapKeys,mapObject,mapString,mapValues,mask,matches,matchesWith,maxBy,maxDate,maxN,median,memoize,merge,midpoint,minBy,minDate,minN,mostPerformant,negate,nest,nodeListToArray,none,nthArg,nthElement,objectFromPairs,objectToPairs,observeMutations,off,offset,omit,omitBy,on,once,onUserInputChange,orderBy,over,overArgs,pad,palindrome,parseCookie,partial,partialRight,partition,percentile,permutations,pick,pickBy,pipeAsyncFunctions,pipeFunctions,pluralize,powerset,prefix,prettyBytes,primes,promisify,pull,pullAtIndex,pullAtValue,pullBy,radsToDegrees,randomHexColorCode,randomIntArrayInRange,randomIntegerInRange,randomNumberInRange,readFileLines,rearg,recordAnimationFrames,redirect,reducedFilter,reduceSuccessive,reduceWhich,reject,remove,removeNonASCII,renameKeys,reverseString,RGBToHex,round,runAsync,runPromisesInSeries,sample,sampleSize,scrollToTop,sdbm,serializeCookie,setStyle,shallowClone,shank,show,shuffle,similarity,size,sleep,smoothScroll,sortCharactersInString,sortedIndex,sortedIndexBy,sortedLastIndex,sortedLastIndexBy,splitLines,spreadOver,stableSort,standardDeviation,stringPermutations,stripHTMLTags,sum,sumBy,sumPower,symmetricDifference,symmetricDifferenceBy,symmetricDifferenceWith,tail,take,takeRight,takeRightWhile,takeWhile,throttle,times,timeTaken,toCamelCase,toCurrency,toDecimalMark,toggleClass,toHash,toKebabCase,tomorrow,toOrdinalSuffix,toSafeInteger,toSnakeCase,toTitleCase,transform,triggerEvent,truncateString,truthCheckCollection,unary,uncurry,unescapeHTML,unflattenObject,unfold,union,unionBy,unionWith,uniqueElements,uniqueElementsBy,uniqueElementsByRight,uniqueSymmetricDifference,untildify,unzip,unzipWith,URLJoin,UUIDGeneratorBrowser,UUIDGeneratorNode,validateNumber,when,without,words,xProd,yesNo,zip,zipObject,zipWith,binarySearch,celsiusToFahrenheit,cleanObj,collatz,countVowels,factors,fahrenheitToCelsius,fibonacciCountUntilNum,fibonacciUntilNum,heronArea,howManyTimes,httpDelete,httpPut,isArmstrongNumber,isSimilar,JSONToDate,kmphToMph,levenshteinDistance,mphToKmph,pipeLog,quickSort,removeVowels,solveRPN,speechSynthesis,squareSum} + diff --git a/test/chainAsync.test.js b/test/chainAsync.test.js index ba640c625..4a01542c5 100644 --- a/test/chainAsync.test.js +++ b/test/chainAsync.test.js @@ -5,18 +5,30 @@ test('chainAsync is a Function', () => { expect(chainAsync).toBeInstanceOf(Function); }); +let incrementer = 0; test('Calls all functions in an array', () => { + chainAsync([ + next => { + incrementer += 1; + next(); + }, + next => { + incrementer += 1; + next(); + }, + next => { + expect(incrementer).toEqual(2); + } + ]); +}); + +test('Last function does not receive "next" argument', () => { chainAsync([ next => { next(); }, next => { - (() => { - next(); - })(); - }, - next => { - expect(true).toBeTruthy(); + expect(next).toBe(undefined); } ]); }); diff --git a/vscode_snippets/snippets.json b/vscode_snippets/snippets.json index 7d5c342c6..b9351000e 100644 --- a/vscode_snippets/snippets.json +++ b/vscode_snippets/snippets.json @@ -208,7 +208,11 @@ "body": [ "const chainAsync = fns => {", " let curr = 0;", - " const next = () => fns[curr++](next);", + " const last = fns[fns.length - 1];", + " const next = () => {", + " const fn = fns[curr++];", + " fn === last ? fn() : fn(next);", + " };", " next();", "};" ],