阅读源码

Mr.Hotsuitor大约 2 分钟source codesource codejavascript

阅读源码

  • 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: