Skip to content

Commit

Permalink
something
Browse files Browse the repository at this point in the history
  • Loading branch information
yk committed May 5, 2024
1 parent a556682 commit e65ddae
Show file tree
Hide file tree
Showing 38 changed files with 1,175 additions and 881 deletions.
86 changes: 45 additions & 41 deletions content/posts/base-js/curry和compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ tags: [JavaScript]
1. 声明式编程
比如 map 只关注输入和结果,用表达式来描述程序逻辑, 而 常规的 for 循环命令式控制过程还关注具体控制和状态变化
2. 纯函数
1. 仅取决于提供的输入
2. 不会造成或超出其作用域的变化。 比如修改全局对象或引用传递的参数,打印日志,时间等
1. 仅取决于提供的输入
2. 不会造成或超出其作用域的变化。 比如修改全局对象或引用传递的参数,打印日志,时间等
3. 引用透明
一个函数对于相同的输入始终产生相同的结果,那就是引用透明的
4. 不可变性
Expand All @@ -25,47 +25,50 @@ tags: [JavaScript]

### curry

柯里化,其实就是针对函数多个参数的处理,把多个参数变为可以分步接收计算的方式。形如`f(a,b,c) -> f(a)(b)(c)`
柯里化,其实就是针对函数多个参数的处理,把多个参数变为可以分步接收计算的方式。

`f(a,b,c) -> f(a)(b)(c)`

实现也比较简单:

```javascript
```js {lineNos=false}
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function (...args2) {
return curried.apply(this, args.concat(args2)); // 借助curried累加参数
};
const argLen = fn.length
return function curried(...args) {
if (args.length === argLen) {
return fn.call(this, ...args)
} else {
return function (...args2) {
return curried.call(this, ...[...args, ...args2]) // 累加参数
}
}
}
};
}

// test
const sum = (a, b, c) => a + b + c;
const currySum = curry(sum);
console.log(currySum(1, 2, 3)); // 6
console.log(currySum(1)(2, 3)); // 6
console.log(currySum(1, 2)(3)); // 6
const sum = (a, b, c) => a + b + c
const currySum = curry(sum)
console.log(currySum(1, 2, 3)) // 6
console.log(currySum(1)(2, 3)) // 6
console.log(currySum(1, 2)(3)) // 6
```

柯里化的孪生兄弟 -- 偏函数,个人见解:
就是在 curry 之上,初始化时固定了部分的参数,注意两者累加参数的方式不太一样哦。

```js
```js {lineNos=false}
/**
* args 为固定的参数
*/
function partial(fn, ...args) {
return function (...args2) {
const _args = args.concat(args2);
if (_args.length >= fn.length) {
return fn.apply(this, _args);
} else {
return partial.call(this, fn, ..._args); // 借助 partial 累加参数
return function (...args2) {
const _args = args.concat(args2)
if (_args.length >= fn.length) {
return fn.apply(this, _args)
} else {
return partial.call(this, fn, ..._args) // 借助 partial 累加参数
}
}
};
}

// test
Expand All @@ -78,18 +81,19 @@ console.log(partialSum(1)(2)) // 103

compose 合成函数是把多层函数嵌套调用扁平化,内部函数执行的结果作为外部面函数的参数。

```js
function compose() {
// 可以加个判断参数是否合格, 此处省略
const fns = [...arguments]
return function () {
let lastIndex = fns.length - 1
let res = fns[lastIndex].call(this, ...arguments)
while (lastIndex--) {
res = fns[lastIndex].call(this, res)
`a(b(c(d()))) -> compose(a,b,c,d)`

```js {lineNos=false}
function compose(...fns) {
return function (...args) {
let fnsLen = fns.length
let lastIndex = fnsLen - 1
let res = fns[lastIndex].call(this, ...args)
while (lastIndex--) {
res = fns[lastIndex].call(this, res)
}
return res
}
return res
}
}

// test
Expand All @@ -104,14 +108,14 @@ console.log(test(10, 20)) // 40

与之对应的是 pipe 函数,只不过是从左往右执行。这里就用 reduce 来实现一下吧:

```js
```js {lineNos=false}
function pipe(...fns) {
return function (args) {
return fns.reduce((t, cb) => cb(t), args);
};
return function (args) {
return fns.reduce((t, cb) => cb(t), args)
}
}
```

## 参考

- [函数式编程入门教程](http://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html)
- [函数式编程入门教程](http://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html)
87 changes: 45 additions & 42 deletions content/posts/base-js/promise并发.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,48 @@ date: 2022-09-20T14:41:20+08:00
tags: [JavaScript, promise]
---

一个比较经典的问题,就是 n 个 请求,实现一个方法,让每次并发请求个数是 x 个这样子
一个比较经典的问题,就是 n 个 请求,实现一个方法,让每次并发请求个数是 x
其实在前端中应该是比较常用的应用,如果 n 个请求瞬间被发送到后端,这个是不合理的,应该控制在一定的范围内,当某个请求返回时,再去发起下个请求。

## promise

关键点,一个限定数量的请求池,一个 promise 有结果后,再去加入下一个请求,递归直到所有结束。

```js
const mockReq = function (time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time);
}, time);
});
};
const reqList = [1000, 2000, 2000, 3000, 3000, 5000];
const res = [];
```js {open=true, lineNos=false, wrap=true, header=true, title=""}
const mockReq = time => {
return function () {
return new Promise(resolve => {
setTimeout(() => {
resolve(time)
}, time)
})
}
}

const reqList = [1000, 3000, 2000, 2000, 3000, 4000, 2000]
const reqs = reqList.map(item => mockReq(item))

/**
* 核心就是利用递归调用请求,在then回调中自动加入请求池
*/
function concurrentPromise(limit) {
const pool = [];
for (let i = 0; i < limit; ++i) {
// pool.push(mockReq(reqList.shift());
managePool(pool, mockReq(reqList.shift()));
}
const res = []
function concurrent(reqs, limit) {
const pool = []
for (let i = 0; i < limit; ++i) {
// pool.push(reqs[i]())
poolControl(pool, reqs.shift(), reqs)
}
}
function managePool(pool, promise) {
pool.push(promise);
promise.then((r) => {
res.push(r);
pool.splice(pool.indexOf(promise), 1);
if (reqList.length) managePool(pool, mockReq(reqList.shift()));
pool.length === 0 && console.log('ret', res);
});
function poolControl(pool, req, reqs) {
pool.push(req)
req().then(r => {
res.push(r)
pool.splice(pool.indexOf(req), 1)
if (reqs.length) poolControl(pool, reqs.shift(), reqs)
if (pool.length === 0) console.log('🔥🔥🔥 ---', res)
})
}
concurrentPromise(3);
concurrent(reqs, 3)
```

重点是:
Expand All @@ -53,23 +57,22 @@ concurrentPromise(3);

通过 aync + promise.race 能更简单的控制。

```js
async function concurrentPromise(limit) {
const pool = [];
for (let i = 0; i < reqList.length; ++i) {
const req = mockReq(reqList[i]);
pool.push(req);
req.then((r) => {
res.push(r);
pool.splice(pool.indexOf(req), 1);
if (pool.length === 0) console.log(res);
});
if (pool.length === limit) {
await Promise.race(pool);
```js {open=true, lineNos=false, wrap=true, header=true, title=""}
async function concurrent(reqs, limit) {
const pool = []
for (let i = 0; i < reqs.length; ++i) {
pool.push(reqs[i]())
reqs[i]().then(r => {
res.push(r)
pool.splice(pool.indexOf(reqs[i]), 1)
if (pool.length === 0) console.log('🔥🔥🔥 ---', res)
})
if (pool.length === limit) {
await Promise.race(pool)
}
}
}
}
concurrentPromise(3);
concurrent(reqs, 3)
```

关键点:与原生 promise 维护一个请求池不同的是,直接通过**普通 for** 循环添加 await 和 Promise.race 来实现等待效果。
Expand All @@ -80,4 +83,4 @@ concurrentPromise(3);

## 参考

- [async-pool](https://github.com/rxaviers/async-pool/blob/master/lib/es9.js)
- [async-pool](https://github.com/rxaviers/async-pool/blob/master/lib/es9.js)
114 changes: 61 additions & 53 deletions content/posts/base-js/promise提前结束.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,60 @@ tags: [JavaScript, promise]

axios 已经有了取消请求的功能:

```js
const CancelToken = axios.CancelToken;
let cancel;
```js {open=false, lineNos=false, wrap=true, header=true, title="已废弃"}
const CancelToken = axios.CancelToken
let cancel

axios.get('/demo', {
cancelToken: new CancelToken((c) => {
cancel = c;
})
});
cancelToken: new CancelToken(c => {
cancel = c
})
})

cancel(); // 取消这个请求
cancel() // 取消这个请求

/* ---------- 或者 ---------- */
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});

axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
const CancelToken = axios.CancelToken
const source = CancelToken.source()

axios
.get('/user/12345', {
cancelToken: source.token
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message)
} else {
// 处理错误
}
})

axios.post(
'/user/12345',
{
name: 'new name'
},
{
cancelToken: source.token
}
)

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
source.cancel('Operation canceled by the user.')
```

上方的 CancelToken 从 v0.22.0 版本被废弃,最新的 axios 使用的是 fetch api `AbortController`:
上方的 CancelToken 从 v0.22.0 版本被废弃,最新的 axios 使用的是与 fetch 一样的 api `AbortController`:

```js
const controller = new AbortController();
```js {open=true, lineNos=false, wrap=true, header=true, title=""}
const controller = new AbortController()

axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
axios
.get('/foo/bar', {
signal: controller.signal
})
.then(function (response) {
//...
})
// 取消请求
controller.abort()
```
Expand All @@ -62,24 +70,24 @@ controller.abort()

首先我在"中断"上打了引号,因为 promise 一旦创建是无法取消的,所谓的”中断“,只是在合适的时候,提前结束 pending 状态而已,所以本文题目才是提前结束而不是“中断”。

```js
```js {open=true, lineNos=false, wrap=true, header=true, title=""}
const mockReq = function (time) {
const req = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time);
}, time);
});
let abort = null;
const stopHandler = new Promise((resolve, reject) => {
abort = reject;
});
const p = Promise.race([req, stopHandler]);
p.abort = abort;
return p;
};

mockReq(2000).then((res) => console.log(res));
mockReq().abort();
const req = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time)
}, time)
})
let abort = null
const stopHandler = new Promise((resolve, reject) => {
abort = reject
})
const p = Promise.race([req, stopHandler])
p.abort = abort
return p
}

mockReq(2000).then(res => console.log(res))
mockReq().abort()
```

其实核心就是借助另一个永远不会成功或失败的 promise 的 execute 函数,来让人可以手动改变 handle 的状态,进而影响 `Promise.race` 的执行。值得注意的是,req 仍然会执行完成,但是已经无意义了。
Expand All @@ -88,4 +96,4 @@ promise 超时控制原理一样,把一个设定超时时间的 promise 和正

## 参考

- [axios 文档-取消请求](https://www.axios-http.cn/docs/cancellation)
- [axios 文档-取消请求](https://www.axios-http.cn/docs/cancellation)
Loading

0 comments on commit e65ddae

Please sign in to comment.