The preface

Concurrency is a piece of logic that we deal with when we need to make multiple requests at the same time and wait for all of them to return. When we need to make multiple requests at the same time and need to handle the first request logic that returns the fastest, it is a race state. I’m sure good readers have dealt with this at some point. However, how to deal with this kind of problem in functional programming is rarely mentioned. This article will briefly introduce how to deal with these two kinds of problems through functional programming and the function between functors to deal with the concurrency part written in another part of functional programming concurrency, which focuses on the introduction of the function and principle of AP function.

Const createTaskByNumber = n => new Task((rej, res) => {console.log(n); const createTaskByNumber = n => new Task((rej, res) => {console.log(n); setTimeout(() => res(n), n * 1000); }); const [http1, http3, http5] = [1, 3, 5].map(n => createTaskByNumber(n));Copy the code

So we have requests that are waiting 1/3/5 seconds for a response. The Task here comes from data. Task, which I have mentioned in previous articles.

now

Before we get to that, let’s compare the way things were done in ancient times.

Const collect: any[] = []; [http1, http3, http5].forEach((http, index) => { http().then(res => { collect[index] = res; if (collect.length === 3) { const [x, y, z] = collect; console.log(x + y + z); }}); }); Const collect: any[] = []; [http1, http3, http5].forEach((http, index) => { http().then(res => { collect[index] = res; if (collect.length === 1) { console.log(res); }}); });Copy the code

The principle is simple: execute business logic by maintaining an array and determining whether the contents of the array are all requested, thus handling concurrency and race states. The downside, however, is that we need to maintain more variables and the resulting maintenance costs.

Fortunately, ES6 brings us the most precious gift of all, promise.With promise.all&promise.race. Dealing with such problems has become more handy.

All ([http1, http3, http5]). Then (res => {const [x, y, z] = res; console.log(x + y + z) }); // Handle promise.race ([http1, http3, http5]). Then (res => {console.log(res); });Copy the code

Well, after the introduction of the current common solution, also should look at the functional programming scheme, may be a little disappointed, if you want to use functional and functor to deal with the above two requirements, the need for operation and understanding costs are not low, but it doesn’t matter, I will carefully explain, carefully to the source level.

Task.prototype.concat

Concat is relatively simple compared to an AP that handles concurrency. Data. task notes concat as follows:

/ * * * Selects the earlier of the two tasks ` Task [alpha, beta] ` * * @ the summary @ Task (alpha, beta) = > Task (alpha, beta) - > Task (alpha, beta) * /Copy the code

To resolve the first of the two functors, consider the following use case.

const {error, log} = console; http1.concat(http3).concat(http5).fork(error, log); / / 1Copy the code

The second thing is that the name concat is a little hard to understand. At first glance, it sounds like an array or character method, and we want to move closer to the standard, to promise.race. Encapsulate a race function

function race(arr: any[]) { return new Task((rej, res) => { const task = arr.reduce((a, b) => { return a.concat(b); }); task.fork(rej, res); }); } const handleAllHttp = curry((f, https) => compose(fork(error.f), race, () => https); Const add = handleAllHttp(x => x + 1); const multiply= handleAllHttp(x => x * x); add([http1, http3, http5]); // 2 multiply([http1, http3, http5]); / / 1Copy the code

The principle of CONCAT is much simpler than that of AP. Is it difficult to understand CONCAT after understanding AP

Task.prototype. Concat = function _concat(that) {var forkThis = this.fork; // Save the current task forkThat = that.fork; // Save tasks that are concated. var cleanupThis = this.cleanup; var cleanupThat = that.cleanup; function cleanupBoth(state) { cleanupThis(state[0]); cleanupThat(state[1]); Return new task (function(reject, resolve) {var done = false; // Check whether the task is resolved var allState; ForkThis (reject (reject), forkThis(resolve)); Var thatState = forkThat(reject (reject), resolve (resolve)); return allState = [thisState, thatState]; // Update done when this function is called by resolve in the functor and return the result of new Task resolve function(x) { if (! Done) {// This is important when there is a chain call or concat multiple functors // Since this function always maintains a defined closure // this function will guard the resolve of each functor that passes through it Done = true; done = true; delayed(function(){ cleanupBoth(allState) }) return f(x); }}; } }, cleanupBoth); };Copy the code

conclusion

little function, large magic~