From 251c931083985c8d5929e8c0b992f79d9b9431ae Mon Sep 17 00:00:00 2001 From: seognil LC Date: Sat, 11 Apr 2020 17:47:55 +0800 Subject: [PATCH] feat(add): async programming --- .../parallel/parallel-limit-version-2.ts | 39 +++++++++++++++ .../parallel/parallel-limit.ts | 30 ++++++++++++ js/async-programming/single-lock/key-lock.ts | 47 +++++++++++++++++++ .../single-lock/single-lock.ts | 39 +++++++++++++++ js/async-programming/tsconfig.json | 11 +++++ js/async-programming/util/delay.ts | 1 + 6 files changed, 167 insertions(+) create mode 100644 js/async-programming/parallel/parallel-limit-version-2.ts create mode 100644 js/async-programming/parallel/parallel-limit.ts create mode 100644 js/async-programming/single-lock/key-lock.ts create mode 100644 js/async-programming/single-lock/single-lock.ts create mode 100644 js/async-programming/tsconfig.json create mode 100644 js/async-programming/util/delay.ts diff --git a/js/async-programming/parallel/parallel-limit-version-2.ts b/js/async-programming/parallel/parallel-limit-version-2.ts new file mode 100644 index 0000000..dd32446 --- /dev/null +++ b/js/async-programming/parallel/parallel-limit-version-2.ts @@ -0,0 +1,39 @@ +import { delay } from '../util/delay'; + +type AsyncFunction = () => Promise; +const limitOf = (max: number) => { + const mq: AsyncFunction[] = []; + let count = 0; + + const runTask = async () => { + if (count < max) { + count++; + await mq.shift()!(); + count--; + checkMq(); + } + }; + + const checkMq = () => { + if (mq.length) runTask(); + }; + + return (fn: AsyncFunction) => { + mq.push(fn); + checkMq(); + }; +}; + +// * ================================================================================ + +const limit = limitOf(2); + +console.warn('start'); + +for (let i = 1; i < 10; i++) { + limit(async () => { + await delay(500); + // await delay(Math.random() * 500 + 100); + console.warn(i); + }); +} diff --git a/js/async-programming/parallel/parallel-limit.ts b/js/async-programming/parallel/parallel-limit.ts new file mode 100644 index 0000000..614c20a --- /dev/null +++ b/js/async-programming/parallel/parallel-limit.ts @@ -0,0 +1,30 @@ +import { delay } from '../util/delay'; + +type AsyncFunction = () => Promise; + +const limitOf = (max: number) => { + const mq: AsyncFunction[] = []; + let count = 0; + + const runTask = async (fn: AsyncFunction) => { + count++; + await fn(); + count--; + if (mq.length) runTask(mq.shift()!); + }; + + return (fn: AsyncFunction) => { + count < max ? runTask(fn) : mq.push(fn); + }; +}; + +// * ================================================================================ + +const limit = limitOf(2); + +for (let i = 1; i < 10; i++) { + limit(async () => { + await delay(500); + console.warn(i); + }); +} diff --git a/js/async-programming/single-lock/key-lock.ts b/js/async-programming/single-lock/key-lock.ts new file mode 100644 index 0000000..a1a14a5 --- /dev/null +++ b/js/async-programming/single-lock/key-lock.ts @@ -0,0 +1,47 @@ +// * 也可以改成 Map[]>,数组的形式,当最后一个任务完成后(manualRes 调用时)删除 key,以便节约内存 +const chainMap = new Map>(); + +const keyLock = async (key: string) => { + if (!chainMap.get(key)) chainMap.set(key, Promise.resolve()); + const chain = chainMap.get(key)!; + + let prevPromise = chain; + + // * 将 resolve 提到外部,以便实现手动触发 + let waitingRes: Function; + let nextPromise = new Promise((res) => { + waitingRes = res; + }); + + chainMap.set( + key, + chain.then(() => nextPromise), + ); + + await prevPromise; + + return waitingRes!; +}; + +// * ================================================================================ + +import { delay } from '../util/delay'; + +async function process(key: string, msg?: string) { + let release = await keyLock(key); + console.log('get locker', key, msg); + + await delay(1000); + + console.log('end locker', key, msg); + release(); +} + +process('key1', 'dog'); +process('key1', 'cat'); +process('key2', 'Macbook'); +// process('key3', 'JavaScript'); +// process('key3', 'TypeScript'); +// process('key3', 'ClojureScript'); +// process('key3', 'Rust'); +// process('key3', 'Haskell'); diff --git a/js/async-programming/single-lock/single-lock.ts b/js/async-programming/single-lock/single-lock.ts new file mode 100644 index 0000000..a2ccbc1 --- /dev/null +++ b/js/async-programming/single-lock/single-lock.ts @@ -0,0 +1,39 @@ +// * 也可以做成高阶函数,以便生成多个 lock 独立运作 + +let chain = Promise.resolve(); + +export const singleLock = async () => { + let prevPromise = chain; + + // * 将 resolve 提到外部,以便实现手动触发 + let manualRes: Function; + let nextPromise = new Promise((res) => { + manualRes = res; + }); + + chain = chain.then(() => nextPromise); + + await prevPromise; + + return manualRes!; +}; + +// * ================================================================================ + +import { delay } from '../util/delay'; + +async function process(msg: string) { + let release = await singleLock(); + console.log('get locker', msg); + + await delay(1000); + + console.log('end locker', msg); + release(); +} + +process('JavaScript'); +process('TypeScript'); +process('ClojureScript'); +process('Rust'); +process('Haskell'); diff --git a/js/async-programming/tsconfig.json b/js/async-programming/tsconfig.json new file mode 100644 index 0000000..0fd1e54 --- /dev/null +++ b/js/async-programming/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "CommonJS", + "esModuleInterop": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "strict": true, + "allowJs": true + }, +} \ No newline at end of file diff --git a/js/async-programming/util/delay.ts b/js/async-programming/util/delay.ts new file mode 100644 index 0000000..6cfe4b8 --- /dev/null +++ b/js/async-programming/util/delay.ts @@ -0,0 +1 @@ +export const delay = (ms: number = 100) => new Promise((res) => setTimeout(res, ms));