阅读源码
大约 2 分钟
阅读源码
- why
- what
- how
阅读源码是一种高效的学习方式,通过阅读优秀的源码,能提高自己的编程水平。
why必要性
阅读源码等于学习他人的经验。编程需要非常多的动手实践,我们往往简单学习了一门编程语言,就能入门,就能写出 “Hello World!”,而且迫不及待地写下一个demo,“Todo List”。就好像我们学汉字一样,很容易就学会了常用汉语300词,但是我们能写出《唐诗宋词300首》吗?答案是否定的。为了提供文学水平,大量阅读经典文学作品就很有必要。同理,为了提供编程水平,大量阅读优秀的源码也是非常有必要的。
借鉴前人的经验,不必重复造轮子。把时间和精力花在更重要的事情上。
what
是阅读程序的源代码。一般在 github 可以阅读到全世界优秀人士写的代码,看到优秀的示例代码可以直接copy过来使用。 模仿也是一种学习方式。
如何阅读
1.下载源码,本地运行 2.按照官方示例,使用示例 3.查看源码目录,大致浏览整体的源码目录文件。阅读由粗到细 3.1 查看 packag.json 4.查看测试代码【可选】 5.本地单步调试,跟着代码运行流程阅读
原则:先粗后细一定要画图,能把流程图画出来,才算真正看懂了源码
案例
控制并发请求库 p-limt
import Queue from 'yocto-queue';
export default function pLimit(concurrency) {
// 校验传入的并发数值
validateConcurrency(concurrency);
const queue = new Queue();
let activeCount = 0;
// next() -> 出队列,执行下一个任务
const resumeNext = () => {
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
// Since `pendingCount` has been decreased by one, increase `activeCount` by one.
activeCount++;
}
};
// 执行队列里的下一个任务
const next = () => {
activeCount--;
resumeNext();
};
// 执行队列任务
const run = async (function_, resolve, arguments_) => {
const result = (async () => function_(...arguments_))();
resolve(result);
try {
await result;
} catch {}
next();
};
// 装饰enqueue方法,入队列的是用 Promise 包装后的任务执行函数
// 参数传入的 resolve 确保在执行异步任务后,获得正确的resolve回调
const enqueue = (function_, resolve, arguments_) => {
// Queue `internalResolve` instead of the `run` function
// to preserve asynchronous context.
new Promise(internalResolve => {
queue.enqueue(internalResolve);
}).then(
run.bind(undefined, function_, resolve, arguments_),
);
(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// after the `internalResolve` function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
await Promise.resolve();
if (activeCount < concurrency) {
resumeNext();
}
})();
};
// 执行Promise的 生成器
const generator = (function_, ...arguments_) => new Promise(resolve => {
enqueue(function_, resolve, arguments_);
});
// 使用 defineProperties 增强数据封装,
// 实现私有属性
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value() {
queue.clear();
},
},
concurrency: {
get: () => concurrency,
set(newConcurrency) {
validateConcurrency(newConcurrency);
concurrency = newConcurrency;
queueMicrotask(() => {
// eslint-disable-next-line no-unmodified-loop-condition
while (activeCount < concurrency && queue.size > 0) {
resumeNext();
}
});
},
},
});
return generator;
}
function validateConcurrency(concurrency) {
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
}
pixijs
todo: