diff --git a/TODO1/5-best-practices-to-prevent-git-leaks.md b/TODO1/5-best-practices-to-prevent-git-leaks.md
index 933e9f236a9..24f7fe096f2 100644
--- a/TODO1/5-best-practices-to-prevent-git-leaks.md
+++ b/TODO1/5-best-practices-to-prevent-git-leaks.md
@@ -1,88 +1,90 @@
-> * 原文地址:[5 Best Practices To Prevent Git Leaks](https://levelup.gitconnected.com/5-best-practices-to-prevent-git-leaks-4997b96c1cbe)
-> * 原文作者:[Coder’s Cat](https://medium.com/@coderscat)
-> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
-> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/5-best-practices-to-prevent-git-leaks.md](https://github.com/xitu/gold-miner/blob/master/TODO1/5-best-practices-to-prevent-git-leaks.md)
-> * 译者:
-> * 校对者:
+> - 原文地址:[5 Best Practices To Prevent Git Leaks](https://levelup.gitconnected.com/5-best-practices-to-prevent-git-leaks-4997b96c1cbe)
+> - 原文作者:[Coder’s Cat](https://medium.com/@coderscat)
+> - 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
+> - 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/5-best-practices-to-prevent-git-leaks.md](https://github.com/xitu/gold-miner/blob/master/TODO1/5-best-practices-to-prevent-git-leaks.md)
+> - 译者:[YueYongDEV](https://github.com/YueYongDev)
+> - 校对者:[Roc](https://github.com/QinRoc)、[icy](https://github.com/Raoul1996)
-# 5 Best Practices To Prevent Git Leaks
+# 防止 Git 泄漏的 5 种最佳做法
-![Photo by Clint Patterson on Unsplash](https://cdn-images-1.medium.com/max/4000/0*bskmb4Tr98q5if_y.jpg)
+![](https://cdn-images-1.medium.com/max/4000/0*bskmb4Tr98q5if_y.jpg)
-Countless developers are using Git for version control, but many don’t have enough knowledge about how Git works. Some people even use Git and Github as tools for backup files. This leads to information disclosure in Git repositories. [Thousands of new API or cryptographic keys leak via GitHub projects every day.](https://www.zdnet.com/article/over-100000-github-repos-have-leaked-api-or-cryptographic-keys/)
+无数的开发人员正在使用 Git 进行版本控制,但是许多开发人员对 Git 的工作方式并没有足够的了解。有些人甚至将 Git 和 Github 用作备份文件的工具。这些做法导致 Git 仓库中的信息遭到泄露,[每天都有数千个新的 API 或加密密钥从 GitHub 泄漏出去](https://www.zdnet.com/article/over-100000-github-repos-have-leaked-api-or-cryptographic-keys/)。
-I have been working in the field of information security for three years. About two years ago, our company had a severe security issue triggered by the information leak in a Git repository.
+我在信息安全领域工作了三年。大约在两年前,我们公司发生了一起非常严重的安全问题,是由于 Git 仓库发生了信息泄露导致的。
-An employee accidentally leaked an AWS key to Github. The attacker used this key to download more sensitive data from our servers. We put a lot of time into fixing this issue, we tried to find out how much data leaked, analyzed the affected systems and related users, and replaced all the leaked keys in systems.
+一名员工意外地在 Github 上泄露了 AWS 的密钥。攻击者使用此密钥从我们的服务器下载很多敏感的数据。我们花了很多时间来解决这个问题,我们试图统计出泄漏了多少数据,并分析了受影响的系统和相关用户,最后替换了系统中所有泄漏的密钥。
-It is a sad story that any company and developer would not want to experience.
+这是一个任何公司和开发人员都不愿经历的悲惨故事。
-I won’t write more details about it. Instead, I hope more people know how to avoid it. Here are my suggestions for you to keep safe from Git leaks.
+关于整件事情的细节我就不多写了。事实上,我希望更多的人知道如何去避免 Git 的信息泄露。以下是我提出的一些建议。
-## Build security awareness
+## 建立安全意识
-Most junior developers don’t have enough security awareness. Some companies will train new employees, but some companies don’t have systematic training.
+大多数新人开发者没有足够的安全意识。有些公司会培训新员工,但有些公司没有系统的培训。
-As a developer, we need to know which kind of data may introduce security issues. Remember these categories of data can not be checked into Git repository:
+作为开发人员,我们需要知道哪些数据可能会带来安全问题。千万记住,下面这些数据不要上传到 Git 仓库中:
-1. Any configuration data, including password, API keys, AWS keys, private keys, etc.
-2. [Personally Identifiable Information](https://en.wikipedia.org/wiki/Personal_data) (PII). According to GDPR, if a company leaked the users’ PII, the company needs to notify users, relevant departments and there will be more legal troubles.
+1. 任何配置数据,包括密码,API 密钥,AWS 密钥和私钥等。
+2. [个人身份信息](https://en.wikipedia.org/wiki/Personal_data)(PII)。根据 GDPR 的说法,如果公司泄露了用户的 PII,则该公司需要通知用户和有关部门,否则会带来更多的法律麻烦。
-If you are working for a company, don’t share any source code or data related to the company without permission.
+如果你在公司工作,未经允许,请勿共享任何与公司相关的源代码或数据。
-Attackers can easily find some code with a company copyright on GitHub, which was accidentally leaked to Github by employees.
+攻击者可以在 GitHub 上轻松地找到某些具有公司版权的代码,而这些代码都是被员工无意中泄露到 Github 上的。
-My advice is, try to distinguish between company affairs and personal stuff strictly.
+我的建议是,应该将公司项目和个人项目严格区分。
-## Use Git ignore
+## 使用 Git 忽略(Git ignore)
-When we create a new project with Git, we must set a **.gitignore** properly. **gitignore** is a Git configuration file that lists the files or directories that will not be checked into the Git repository.
+当我们使用 Git 创建一个新项目时,我们必须正确地设置一个 **.gitignore** 文件。**.gitignore** 是一个 Git 配置文件,它列出了不会被存入 Git 仓库的文件或目录。
-This project’s [gitignore](https://github.com/github/gitignore) is a collection of useful .gitignore templates, with all kinds of programming language, framework, tool or environment.
+[这个 gitignore 项目](https://github.com/github/gitignore) 是一个实际使用着的 .gitignore 模板集合,其中包含对应各种编程语言、框架、工具或环境的配置文件。
-We need to know the pattern matching rules of **gitignore** and add our own rules based on the templates.
+我们需要了解 **gitignore** 的模式匹配规则,并根据模板添加我们自己的规则。
![](https://cdn-images-1.medium.com/max/2000/0*VmEolB6qYNCYr9Wf.png)
-## Check commits with Git hooks and CI
+## 使用 Git 钩子(Git hooks)和 CI 检查提交
-No tools could find out all the sensitive data from a Git repository, but a couple of tools and practices can help.
+没有工具可以从 Git 仓库中找出所有敏感数据,但是有一些工具可以为我们提供帮助。
-[git-secrets](https://github.com/awslabs/git-secrets) and [talisman](https://github.com/thoughtworks/talisman) are similar tools, they are meant to be installed in local repositories as [pre-commit hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). Every change will be checked before committed, pre-commit hooks will reject the commit if they detect a prospective commit may contain sensitive information.
+[git-secrets](https://github.com/awslabs/git-secrets) 和 [talisman](https://github.com/thoughtworks/talisman) 是类似的工具,它们应作为[预提交的钩子](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)(pre-commit hooks)安装在本地代码库中。每次都会在提交之前对更改的内容进行检查,如果钩子检测到预期的提交内容可能包含敏感信息,那它们将会拒绝提交。
-[gitleaks](https://github.com/zricethezav/gitleaks) provides another way to find unencrypted secrets and other unwanted data types in git repositories. We could integrate it into automation workflows such as CICD.
+[gitleaks](https://github.com/zricethezav/gitleaks) 提供了另一种在 git 仓库中查找未加密的密钥和其他一些不需要的数据类型的方法。我们可以将其集成到自动化工作流程中,例如 CICD。
-## Code review
+## 代码审查(Code review)
-Code review is a best practice for team working. All the teammates will learn from each other’s source code. Junior developer’s code should be reviewed by developers with more experience.
+代码审查是团队合作的最佳实践。所有队友都将从彼此的源代码中学习。初级开发人员的代码应由具有更多经验的开发人员进行审查。
-Most unintended changes can be found out during the code review stage.
+在代码检查阶段可以发现大多数不符合预期的更改。
-[Enabling branch restrictions](https://help.github.com/en/github/administering-a-repository/enabling-branch-restrictions) can enforce branch restrictions so that only certain users can push to a protected branch in repositories. Gitlab has a similar option.
+[启用分支限制](https://help.github.com/en/github/administering-a-repository/enabling-branch-restrictions) 可以强制执行分支限制,以便只有部分用户才能推送到代码库中受保护的分支。Gitlab 也有类似的选择。
-Setting master to a restricted branch helps us to enforce the code review workflow.
+将 master 设置为受限制的分支有助于我们执行代码审查的工作。
![](https://cdn-images-1.medium.com/max/2208/0*RUqDCQlDgym-Jo8x.png)
-## Fix it quickly and correctly
+## 快速并且正确地修复它
-With all the above tools and mechanisms, errors still could happen. If we fix it quickly and correctly, the leak may introduce no actual security issue.
+即使使用了上面提到的工具和方法,却仍然可能会发生错误。但如果我们快速且正确地修复它,则代码泄漏可能就不会引起实际的安全问题。
-If we find some sensitive data leaked in the Git repository, we can not just make another commit to clean up.
+如果我们在 Git 仓库中发现了一些敏感数据泄漏,我们就不能仅仅通过提交另一个提交覆盖的方式来进行清理。
![This fix is self-deception](https://cdn-images-1.medium.com/max/2000/0*FsGBhHSlXdeSpTk4.png)
-What we need to do is remove all the sensitive data from the entire Git history.
+我们需要做的是从整个 Git 历史记录中删除所有敏感数据。
-**Remember to backup before any cleanup, and then remove the backup clone after we confirmed everything is OK**.
+**在进行任何清理之前请记得进行备份,然后在确认一切正常后再删除备份文件。**
-Use the `--mirror` to clone a bare repository; this is a full copy of the Git database.
+使用 `--mirror` 参数克隆一个仓库;这是 Git 数据库的完整副本。
```bash
git clone --mirror git://example.com/need-clean-repo.git
```
-We need **git filter-branch** to remove data from all branches and commit histories. Suppose we want to remove `./config/passwd` from Git:
+我们需要执行 **git filter-branch** 命令来从所有分支中删除数据并提交历史记录。
+
+下面举个例子,假设我们要从 Git 中删除 `./config /passwd`:
```bash
$ git filter-branch --force --index-filter \
@@ -90,7 +92,7 @@ $ git filter-branch --force --index-filter \
--prune-empty --tag-name-filter cat -- --all
```
-Remember to add the sensitive file to .gitignore:
+请记住将敏感文件添加到 .gitignore 中:
```bash
$ echo "./config/password" >> .gitignore
@@ -98,32 +100,32 @@ $ git add .gitignore
$ git commit -m "Add password to .gitignore"
```
-Then we push all branches to remote:
+然后我们将所有分支推送到远端:
```bash
$ git push --force --all
$ git push --force --tags
```
-Tell our collaborators to rebase:
+告诉我们的小伙伴进行变基(rebase):
```bash
$ git rebase
```
-[BFG](https://rtyley.github.io/bfg-repo-cleaner/) is a faster and simpler alternative to **git filter-branch** for removing sensitive data. It’s usually 10–720x faster than **git filter-branch**. Except for deleting files, BFG could also be used to replace secrets in files.
+[BFG](https://rtyley.github.io/bfg-repo-cleaner/) 是一种比 **git filter-branch** 更快、更简单的用于删除敏感数据的替代方法。通常比 **git filter-branch** 快 10–720 倍。除删除文件外,BFG 还可以用于替换文件中的机密信息。
-BFG will leave the latest commit untouched. It’s designed to protect us from making mistakes. We should explicitly delete the file, commit the deletion, then clean up the history to remove it.
+BFG 保留最新的提交记录。它是用来防止我们犯错误的。我们应该显式地删除文件,提交删除,然后清除历史记录以此删除它。
-If the leaked Git repository is forked by others, we need to follow the [DMCA Takedown Policy](https://help.github.com/en/github/site-policy/dmca-takedown-policy#c-what-if-i-inadvertently-missed-the-window-to-make-changes) to ask Github to remove the forked repositories.
+如果泄漏的 Git 代码库被其他人 fork 了,我们需要遵循 [DMCA](https://help.github.com/en/github/site-policy/dmca-takedown-policy#c-what-if-i-inadvertently-missed-the-window-to-make-changes) 的删除策略,请求 Github 删除创建的代码库。
-The whole procedure requires some time to finish, but it’s the only way to remove all the copies.
+整个过程需要一些时间才能完成,但这是删除所有副本的唯一方法。
-## Conclusion
+## 总结
-Don’t make the same mistake that countless people have made. Try to put some effort to avoid safety accidents.
+不要犯无数人犯过的错误。尽力避免发生安全事故。
-Use these tools and strategies will help much in avoiding Git leaks.
+使用上面提到的这些工具和策略将有助于避免 Git 泄漏。
> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
diff --git a/TODO1/5-better-practices-for-javascript-promises-in-real-projects.md b/TODO1/5-better-practices-for-javascript-promises-in-real-projects.md
new file mode 100644
index 00000000000..e1951105d24
--- /dev/null
+++ b/TODO1/5-better-practices-for-javascript-promises-in-real-projects.md
@@ -0,0 +1,406 @@
+> * 原文地址:[5 Better Practices for JavaScript Promises in Real Projects](https://medium.com/javascript-in-plain-english/5-better-practices-for-javascript-promises-in-real-projects-4917a9daec01)
+> * 原文作者:[bitfish](https://medium.com/@bf2)
+> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
+> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/5-better-practices-for-javascript-promises-in-real-projects.md](https://github.com/xitu/gold-miner/blob/master/TODO1/5-better-practices-for-javascript-promises-in-real-projects.md)
+> * 译者:
+> * 校对者:
+
+# 5 Better Practices for JavaScript Promises in Real Projects
+
+![Photo by [Kelly Sikkema](https://unsplash.com/@kellysikkema?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/10814/0*WrO6pqf5aLgB319V)
+
+After learning the basic usage of Promise, this article hopes to help you better use Promise in real projects.
+
+> Use Promise.all, Promise.race and Promise.prototype.then to improve your code quality.
+
+## Promise.all
+
+Promise.all is actually a promise that takes an array(or an iterable) of promises as an input. Then it gets resolved when all the promises get resolved or any one of them gets rejected.
+
+For example, assume that you have ten promises (Async operation to perform a network call or a database connection). You have to know when all the promises get resolved or you have to wait till all the promises resolve. So you are passing all ten promises to promise.all. Then, Promise.all itself as a promise will get resolved once all the ten promises get resolved or any of the ten promises get rejected with an error.
+
+**Let’s see it in code:**
+
+```js
+Promise.all([promise1, promise2, promise3])
+ .then(result) => {
+ console.log(result)
+ })
+ .catch(error => console.log(`Error in promises ${error}`))
+```
+
+As you can see, we are passing an array to promise.all. And when all three promises get resolved, promise.all resolves and the output is consoled.
+
+**Let’s see an example:**
+
+```JavaScript
+// A simple promise that resolves after a given time
+const timeOut = (t) => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(`Completed in ${t}`)
+ }, t)
+ })
+}
+// Resolving a normal promise.
+timeOut(1000)
+ .then(result => console.log(result)) // Completed in 1000
+// Promise.all
+Promise.all([timeOut(1000), timeOut(2000)])
+ .then(result => console.log(result)) // ["Completed in 1000", "Completed in 2000"]
+```
+
+In the above example, Promise.all resolves after 2000 ms and the output is consoled as an array.
+
+One interesting thing about Promise.all is that the order of the promises is maintained. The first promise in the array will get resolved to the first element of the output array, the second promise will be a second element in the output array, and so on.
+
+OK, the above is the basic usage of promise.all. Let me introduce its application to the real project.
+
+#### 1. Synchronize multiple asynchronous requests
+
+In a real project, a page often needs to send multiple asynchronous requests to the background. And wait until the results in the background return before we start rendering the page.
+
+Some programmers may write code like this:
+
+```JavaScript
+function getBannerList(){
+ return new Promise((resolve,reject)=>{
+ // Suppose we make an asynchronous request to the server
+ setTimeout(function(){
+ resolve('BannerList')
+ },300)
+ })
+}
+
+function getStoreList(){
+ return new Promise((resolve,reject)=>{
+ // Suppose we make an asynchronous request to the server
+ setTimeout(function(){
+ resolve('StoreList')
+ },500)
+ })
+}
+
+function getCategoryList(){
+ return new Promise((resolve,reject)=>{
+ // Suppose we make an asynchronous request to the server
+ setTimeout(function(){
+ resolve('CategoryList')
+ },700)
+ })
+}
+
+getBannerList().then(function(data){
+ // render data
+})
+getStoreList().then(function(data){
+ // render data
+})
+getCategoryList().then(function(data){
+ // render data
+})
+```
+
+The above code does work, but there are two defects in this code:
+
+* Each time we request data from the server, we need to write a separate function to process the data. This will lead to code redundancy and is not convenient for future upgrades and expansion.
+* Each request takes a different amount of time, resulting in functions that render the page three times out of sync, and the user feels the page is stuck.
+
+Now we can use Promise.all to optimize our code.
+
+```JavaScript
+function getBannerList(){
+ // ...
+}
+function getStoreList(){
+ // ...
+}
+function getCategoryList(){
+ // ...
+}
+
+function initLoad(){
+ Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
+ // render datas
+ }).catch(err=>{
+ // ...
+ })
+}
+initLoad()
+```
+
+When all the requests are completed, we process the data uniformly.
+
+#### 2. Handle exceptions
+
+In the example above, we took this approach very directly to handling exceptions:
+
+```js
+Promise.all([p1, p2]).then(res => {
+ // ...
+}).catch(error => {
+ // handle error
+})
+```
+
+As we know, the Promise.all mechanism is that only if any promise instance in the promise array as a parameter throws an exception, then the entire Promise.all function will go directly into the catch method, regardless of whether other promise instances succeed or fail.
+
+But in practice, what we often need is this: even if one or more promise instances throw an exception, we still want Promise.all to continue executing normally. For example, in the above example, even if an exception occurs in `getBannerList()`, we continue to want to execute the program as long as no exception occurs in `getStoreList()` or `getCategoryList()`.
+
+To address this need, we can use a tip to enhance the Promise.all feature. We can write our code in this way:
+
+```JavaScript
+Promise.all([p1.catch(error => error), p2.catch(error => error)]).then(res => {
+ // ...
+}))
+```
+
+This way, even if an exception occurs in one promise instance, it does not interrupt other instances of Promise.all.
+
+Applied to the previous example, this is the result.
+
+```JavaScript
+function getBannerList(){
+ return new Promise((resolve,reject)=>{
+ setTimeout(function(){
+ // Suppose here reject an Error
+ reject(new Error('error'))
+ },300)
+ })
+}
+
+function getStoreList(){
+ // ...
+}
+
+function getCategoryList(){
+ // ...
+}
+
+
+function initLoad(){
+ Promise.all([
+ getBannerList().catch(err=>err),
+ getStoreList().catch(err=>err),
+ getCategoryList().catch(err=>err)
+ ]).then(res=>{
+
+ if(res[0] instanceof Error){
+ // handle error
+ } else {
+ // render data
+ }
+
+ if(res[1] instanceof Error){
+ // handle error
+ } else {
+ // render data
+ }
+
+ if(res[2] instanceof Error){
+ // handle error
+ } else {
+ // render data
+ }
+ })
+}
+
+initLoad()
+```
+
+#### 3. Let multiple promise instances work together
+
+When users try to upload or publish some content, we may need to verify the content provided by users. For example, check whether the content contains bloody violence, pornography, fake news, etc. In many cases, these detection behaviors are performed by different APIs provided by the backend or different cloud functions provided by SaaS service providers.
+
+Some programmers may write code like this:
+
+```JavaScript
+function verify1(content){
+ return new Promise((resolve,reject)=>{
+ // Suppose we perform an asynchronous operation
+ setTimeout(function(){
+ resolve(true)
+ },200)
+ })
+}
+
+function verify2(content){
+ return new Promise((resolve,reject)=>{
+ // Suppose we perform an asynchronous operation
+ setTimeout(function(){
+ resolve(true)
+ },700)
+ })
+}
+
+function verify3(content){
+ // Suppose we perform an asynchronous operation
+ return new Promise((resolve,reject)=>{
+ setTimeout(function(){
+ resolve(true)
+ },300)
+ })
+}
+
+verify1().then(() => {
+ verify2().then(() => {
+ verify3().then(() => {
+ // User content is approved and can be published.
+ }).catch(() => {
+ // User content is not approved and cannot be published.
+ })
+ }).catch(() => {
+ // User content is not approved and cannot be published.
+ })
+}).catch(() => {
+ // User content is not approved and cannot be published.
+})
+```
+
+But with Promise.all, we can make different promise tasks work together:
+
+```JavaScript
+function verify1(content){
+ return new Promise((resolve,reject)=>{
+ // Suppose we perform an asynchronous operation
+ setTimeout(function(){
+ resolve(true)
+ },200)
+ })
+}
+
+function verify2(content){
+ return new Promise((resolve,reject)=>{
+ // Suppose we perform an asynchronous operation
+ setTimeout(function(){
+ resolve(true)
+ },700)
+ })
+}
+
+function verify3(content){
+ // Suppose we perform an asynchronous operation
+ return new Promise((resolve,reject)=>{
+ setTimeout(function(){
+ resolve(true)
+ },300)
+ })
+}
+
+let content = 'some content'
+Promise.all([verify1(content),verify2(content),verify3(content)]).then(result=>{
+ // User content is approved and can be published.
+}).catch(err => {
+ // User content is not approved and cannot be published.
+})
+```
+
+## Promise.race
+
+The parameter of `promise.race` is the same as `promise.all`, which can be a promise array or an iterable object.
+
+The `Promise.race()` method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
+
+#### 4. Timing function
+
+When we request a resource asynchronously from the back-end server, we often limit a time. If no data is received within the specified time, an exception is thrown.
+
+Consider how you would implement this feature? Promise.race can help us solve this problem.
+
+```JavaScript
+function requestImg(){
+ var p = new Promise(function(resolve, reject){
+ var img = new Image();
+ img.onload = function(){
+ resolve(img);
+ }
+ img.src = "https://www.example.com/a.png";
+ });
+ return p;
+}
+
+// Delay function for timing requests
+function timeout(){
+ var p = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ reject('Picture request timeout');
+ }, 5000);
+ });
+ return p;
+}
+
+Promise
+.race([requestImg(), timeout()])
+.then(function(results){
+ // The resource request was completed within the specified time
+ console.log(results);
+})
+.catch(function(reason){
+ // The resource request did not complete within the specified time
+ console.log(reason);
+});
+
+```
+
+## Promise.then
+
+We know that `promise.then()` always returns a promise object, so `promise.then` supports chain calls.
+
+```js
+Promise.then().then().then()
+```
+
+#### 5. Promise Chaining
+
+Therefore, if the amount of data returned by the interface is large and the processing in one then seems bloated, we can consider interviewing the processing logic and executing it in turns in multiple then methods:
+
+```JavaScript
+// Suppose this is the data returned by the backend
+let result = {
+ bannerList:[
+ //...
+ ],
+ storeList:[
+ //...
+ ],
+ categoryList:[
+ //...
+ ],
+ //...
+}
+
+function getInfo(){
+ return new Promise((resolve,reject)=>{
+ setTimeout(()=>{
+ resolve(result)
+ },500)
+ })
+}
+
+getInfo().then(res=>{
+
+ let { bannerList } = res
+
+ // do something with bannerList
+ console.log(bannerList)
+
+ // Return res for the next then method
+ return res
+
+}).then(res=>{
+ let { storeList } = res
+ console.log(storeList)
+ return res
+
+}).then(res=>{
+ let { categoryList } = res
+ console.log(categoryList)
+ return res
+})
+```
+
+> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
+
+---
+
+> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。
diff --git a/TODO1/5-ways-to-create-a-settings-icon.md b/TODO1/5-ways-to-create-a-settings-icon.md
index cc2ac295d90..2a62d6a4e6a 100644
--- a/TODO1/5-ways-to-create-a-settings-icon.md
+++ b/TODO1/5-ways-to-create-a-settings-icon.md
@@ -2,276 +2,276 @@
> * 原文作者:[Helena Zhang](https://medium.com/@minoraxis)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/5-ways-to-create-a-settings-icon.md](https://github.com/xitu/gold-miner/blob/master/TODO1/5-ways-to-create-a-settings-icon.md)
-> * 译者:
-> * 校对者:
+> * 译者:[Jessica](https://github.com/cyz980908)
+> * 校对者:[QinRoc](https://github.com/QinRoc)
-# 5 Ways to Create a Settings Icon
+# 创建一个 Settings icon 的五种方法
-> Learn to use an array of Illustrator features through this exercise
+> 通过这篇文章我们可以学习如何使用 Illustrator 的一系列功能
![](https://cdn-images-1.medium.com/max/4000/1*KXjeemJInI5xQg7HpC2Z9g.png)
-The gear has become a ubiquitous symbol for **settings** in our digital applications.
+齿轮图标已经成为了**设置**符号的标配。
-![Left to right, top to bottom: Google Calendar, Lyft, Dribbble, Facebook, ClassPass, Seamless, Telegram, Reddit, Duolingo, Dropbox, Instagram, Headspace, PayPal, Transit, WeChat](https://cdn-images-1.medium.com/max/4000/1*wu3FKUzPxQhjf4Wk_uCJIw.png)
+![自左向右,从上到下分别是:Google Calendar、Lyft、Dribbble、Facebook、ClassPass、Seamless、Telegram、Reddit、Duolingo、Dropbox、Instagram、Headspace、PayPal、Transit、WeChat](https://cdn-images-1.medium.com/max/4000/1*wu3FKUzPxQhjf4Wk_uCJIw.png)
-There are many interesting ways you can create this icon. We’ll walk though 5 of them in Adobe Illustrator, to pick up techniques you can take forward to any vector drawing.
+有许多有趣的方法可以实现这个图标。我们将学习使用 Adobe Illustrator 实现的 5 个方法,同时还可以学习到适用于任何矢量绘图的技术。
-(**Keyboard shortcuts** are shown in parentheses.)
+(**键盘快捷键**备注在括号中。)
-## Method 1: Rounded Star
+## 方法 1:圆角星
-This simple method is effective for a gear with pointed teeth.
+这种简单的方法能很方便地生成带有尖齿的齿轮。
![](https://cdn-images-1.medium.com/max/3200/1*nUCNYG_c9hNEnMm66rd2zQ.gif)
-Select the **Star Tool** and **click** anywhere on your canvas.
+选择**星型工具**,然后**点击**画布上的任意地方。
![](https://cdn-images-1.medium.com/max/4002/1*qR6U11bjhUffWP7yLvFf9Q.png)
-Play with the parameters.
+填入参数。
![](https://cdn-images-1.medium.com/max/4000/1*gQtrRMFzcjAEudBS1SdQnw.png)
-Round the corners—with the **Direct Selection Tool** (**A**) selected, hover over the shape. **Drag** one of the little circular handles to modify all corners. **Double click** a handle to specify a precise corner radius.
+用**直接选择工具**(**A**)选中圆角,将鼠标悬停在图标上。**拖动**其中的一个小圆形手柄来修改所有的圆角。**双击**手柄可以来定精确的角半径。
![](https://cdn-images-1.medium.com/max/4000/1*KgDXl0u5M1WbrAn5MHPWzQ.png)
-Draw a smaller concentric circle with the **Ellipse Tool** (**L**) to create the eye.
+用**椭圆工具**(**L**)画一个小一些的同心圆来创建中间的小洞。
![](https://cdn-images-1.medium.com/max/4000/1*tmhumNqUD265crJuT7wsCQ.png)
-You can draw the circle free-form or **L** + **click** anywhere on the canvas to specify the exact width and height.
+你可以绘制任意大小的圆圈,或者在画布上的任意位置按**L** + **单击**以指定确切的宽度和高度。
![](https://cdn-images-1.medium.com/max/4000/1*o4LZ-p6BMnXDuuHoth-4sw.png)
-Use the **Transform** panel to make any adjustments to the dimensions.
+可以在“**变换(Transform)**”面板对尺寸进行调整。
![](https://cdn-images-1.medium.com/max/4000/1*taP3_LzZS0jakCB8K2Cg3w.png)
-With both shapes selected, clean up the icon by subtracting the smaller circle from the rounded star (**Pathfinder** panel > **Minus Front**).
+在选中两个图案后,通过从圆角星上减去较小的圆来清理图标(**路径查找器(Pathfinder)** 面板 > **减去前面的图案(Minus Front)**)。
![](https://cdn-images-1.medium.com/max/4000/1*OXarwXairILpFXYEXZ-6IA.png)
-Voilà.
+瞧!完成了!
![](https://cdn-images-1.medium.com/max/4000/1*tmhumNqUD265crJuT7wsCQ.png)
-## Method 2: Zig Zag
+## 方法 2:锯齿形
-Let’s try something different to get to a similar effect.
+让我们尝试一些不同的方法来达到类似的效果。
![](https://cdn-images-1.medium.com/max/3200/1*2tdaBuJsgIuzQgoX2i7EpQ.gif)
-Draw a circle (**L**) with a fill, no stroke.
+绘制一个有填充(fill),没描边(stroke)的圆(**L**)。
![](https://cdn-images-1.medium.com/max/4000/1*0SnYSS0olXodPNhr4LjCPQ.png)
-Select the circle and apply a **Zig Zag Effect** (**Effect** > **Distort & Transform** > **Zig Zag**).
+选择圆并应用**锯齿形效果**(**特效(Effect)** > **扭曲 & 变形(Distort & Transform)** > **锯齿形(Zig Zag)**)。
![](https://cdn-images-1.medium.com/max/4000/1*iHGSBfduKeAKL8BB8ScORw.png)
-Play with the parameters with Preview toggled on.
+在预览选项打开的情况下设置参数。
![](https://cdn-images-1.medium.com/max/4000/1*IpiYKyL7OyyznM4QcVfCwQ.png)
-Now we’ll try a smaller eye. Draw a concentric circle.
+现在我们要试着画一个小一些的同心圆来创建中间的小洞。
![](https://cdn-images-1.medium.com/max/4000/1*Dvsus1_nX3QpU7E3Db8Akg.png)
-Select both shapes. Because we have used an effect, we’ll have to expand the appearance before we merge shapes. Go to **Object** > **Expand Appearance**.
+选择这两个形状。因为我们已经使用了一些特效,所以在合并形状之前必须扩充外观。点击**对象(Object)** > **扩展外观(Expand Appearance)**。
-**Why? Effects are dynamic and non-destructive, which means you can go back and change the parameters at any time. Because of this, effects need to be expanded before performing further shape manipulation.**
+**为什么要这样做?因为特效是可回退的、不会被破坏的,这意味着你可以随时返回并更改参数。因此,在对图形执行进一步的操作之前,需要对特效进行扩展。**
![](https://cdn-images-1.medium.com/max/4000/1*TpvBycrleLcrPgQhobWR2g.png)
-Similar to Method 1, we’ll clean up the icon by subtracting the smaller circle from the larger shape (**Pathfinder** panel > **Minus Front**).
+与方法 1 类似,我们将通过从较大的形状中减去较小的圆来得到图标(**路径查找器(Pathfinder)** 面板 > **减去前面(Minus Front)**)。
![](https://cdn-images-1.medium.com/max/4000/1*Dvsus1_nX3QpU7E3Db8Akg.png)
-## Method 3: Additive Rotation
+## 方法 3:加法和旋转
-Here’s a more complex method that’ll allow us more customization in the gear teeth. We’ll go for a sharper look this time.
+这是一个相对来说更复杂的方法,它允许我们对齿轮进行更多的定制。这次我们要看得更仔细一些。
![](https://cdn-images-1.medium.com/max/3200/1*GzTAZ69HhqNh9bRO_iaifQ.gif)
-Draw a circle with a fill, no stroke.
+绘制一个有填充(fill),没描边(stroke)的圆。
![](https://cdn-images-1.medium.com/max/4000/1*JYSKqVx1RvG33CGiDOKtbg.png)
-Draw a rectangle with the **Rectangle Tool** (**M**) on top, centered to the circle.
+使用在顶部的**矩形工具**(**M**),以圆心为中心绘制一个矩形。
![](https://cdn-images-1.medium.com/max/4000/1*WZZJpZZOQakA5P9-tmCWhw.png)
-'Bulge’ the rectangle. There are many ways to do this. You can select the rectangle and use the **Bulge Effect** (**Effect** > **Warp** > **Bulge**).
+“凸出”矩形。有很多方法可以做到这一点。你可以选择矩形并使用**凸出特效(Bulge Effect)** (**特效(Effect)** > **变形(Warp)** > **凸出(Bulge)**)。
![](https://cdn-images-1.medium.com/max/4000/1*BrNsh1yHIYKrDaMqbYD4iQ.png)
-My preferred method is to add anchor points and use the **Direct Selection Tool** (**A**) to select specific ones to manipulate.
+但我的首选方法是添加锚点,并使用**直接选择工具(Direct Selection Tool)**(**A**)来选择要操作的特定锚点。
![](https://cdn-images-1.medium.com/max/4000/1*B1LwQLNXeZwpBAS6qSiE5w.png)
-To add additional anchor points that are of equal distance from the current ones, select an object and use **Object** > **Path** > **Add Anchor Points**. You can also use the **Pen Tool** (**P**) to manually add points.
+若要添加与当前锚点距离相等的其他锚点,请选择一个对象并使用**对象(Object)** > **路径(Path)** > **添加锚点(Add Anchor Points)**。你也可以使用**钢笔工具**(**P**)手动添加点。
![](https://cdn-images-1.medium.com/max/4000/1*8ipZwTSEe0OYrNY1Ux21zA.png)
-With your shape selected, press **R** for the **Rotate Tool** then **option** + **click** the center of the circle to set that as the reference point. The **Rotate** panel will come up.
+选定形状后,按**R**键选择**旋转工具(Rotate Tool)**,然后按**option**键 + **单击**圆心将其设置为参考点。**旋转**面板就会出现。
![](https://cdn-images-1.medium.com/max/4000/1*ogbTsoIyKr2kYnQjKBe71g.png)
-Choose an angle. A 45° angle will create a gear with 8 teeth (360° divided by 8 is 45°).
+选择一个角度。45° 角会创造出有 8 个齿的齿轮(360° 除以 8 等于 45°)。
-Here’s the fun part.
+接下来有趣的事情发生了。
-Press **Copy** (**not** **OK**). This will copy your shape with the angle and reference point you specified.
+点击 **复制(Copy)** (**注意不是点击** **OK**)。这将会按照你设定的角度和参考点复制你的图案。
![](https://cdn-images-1.medium.com/max/4000/1*CaoipHnbmfFitWDLsSlO_A.png)
-Repeat the action by pressing **Command** + **D** (macOS) or **Ctrl + D** (Windows). Do this twice to complete the circle.
+通过按 **Command** + **D**(macOS)或 **Ctrl** + **D**(Windows)的重复操作,这样做两次后,就可以完成这个圆。
![](https://cdn-images-1.medium.com/max/4000/1*4IyH9hCk6usy8IAmJhuTSA.png)
-Alternatively, you can use the **Transform** **Effect** (**Effect** > **Distort & Transform** > **Transform**) to achieve the same rotational copies.
+或者,你可以使用**变换特效**(**特效(Effect)** > **扭曲 & 变形(Distort & Transform)** > **变换(Transform)** 来实现同样的旋转拷贝。
![](https://cdn-images-1.medium.com/max/4000/1*8JoVfMIcd7fUc4A72VS1PQ.png)
-Effects are non-destructive; whenever you apply one, you can edit it in the **Properties** panel.
+刚刚我们说到,特效(Effect)是不会被破坏的,所以每当你用了一个特效图案,你就可以在**属性**面板中编辑它。
![](https://cdn-images-1.medium.com/max/4000/1*kfB214QfGr1BEvDu6Pg5vQ.png)
-Time to clean up our shapes and add the inner circle.
+接着我们给图案添加内圆。
-Combine all shapes with **Pathfinder** panel > **Unite**.
+点击**路径查找器(Pathfinder)**面板组合 > 合并**(Unite)**,来将所有图形合并。
![](https://cdn-images-1.medium.com/max/4000/1*BJVbsvvgcKtWpKjAH3AEmw.png)
-Draw a smaller concentric circle.
+画一个小一点的同心圆。
![](https://cdn-images-1.medium.com/max/4000/1*Vo4vsPYqBJVfb-IinHiwFw.png)
-Use **Pathfinder** panel > **Minus Front** to subtract the smaller circle from the larger shape.
+点击 **路径查找器(Pathfinder)** 面板 > **减去前面的图案(Minus Front)** 来从较大的形状中减去中心较小的圆。
-Experiment! Different source shapes yield different end results.
+不断尝试!不同的源图形会产生不同的齿轮结果。
![](https://cdn-images-1.medium.com/max/4000/1*ZBAsEntImNjJ4m_GuoMlbw.png)
-## Method 4: Subtractive Rotation
+## 方法 4:减法和旋转
-Method 4 is similar to Method 3.
+方法 4 与方法 3 类似。
![](https://cdn-images-1.medium.com/max/3200/1*cujGoMTZXBrhMfm4WoLMaA.gif)
-Draw a circle with a fill, no stroke.
+绘制一个有填充(fill),没描边(stroke)的圆。
![](https://cdn-images-1.medium.com/max/4000/1*wpX-ymZRcOULMhAEcltU6A.png)
-Draw a small circle aligned to the top.
+画一个与顶部对齐的小圆。
![](https://cdn-images-1.medium.com/max/4000/1*Gd3D7WDFPhMBAjY6R4il5Q.png)
-Select the small circle, press **R** for **Rotate**, then **option** + **click** the center of the circle. Let’s try 6 teeth this time (360°/6). Illustrator will do the calculation for you if you type “360/6” directly.
+选择这个小圆,按 **R** 键进行**旋转**,接着按下 **option** 并同时**点击**圆心。这次我们尝试一下 6 颗齿(360°/ 6)的齿轮。如果直接输入 “360/6”,则 Illustrator 将会自动为你计算。
![](https://cdn-images-1.medium.com/max/4000/1*vRGHB4sIUoU43OuFBOv1ZQ.png)
-Press **Copy**.
+点击复制(**Copy**)。
![](https://cdn-images-1.medium.com/max/4000/1*xkpTHE3W9Y4JcYZ_aV1fyg.png)
-Repeat the action by pressing **Command** + **D** (macOS) or **Ctrl + D** (Windows) 4 times.
+通过按 **Command** + **D**(macOS)或 **Ctrl** + **D**(Windows)4 次重复该操作。
![](https://cdn-images-1.medium.com/max/4000/1*hGWMbewTlFDoxs_B--RICw.png)
-Use **Pathfinder** panel > **Minus Front** to subtract the small circles from the big circle.
+使用**路径查找器(Pathfinder)** 面板 > **减去前面的图案(Minus Front)**,从大的圆中减去小的圆。
-Let’s round the corners. With the **Direct Selection Tool** (**A**), **click** + **drag** the little dots to adjust the corner radiuses.
+接着我们给拐角加一些圆角。使用**直接选择工具(Direct Selection Tool)**(**A**),拖动小圆点来调整圆角的半径。
![](https://cdn-images-1.medium.com/max/4000/1*ZmT0d39tbi01GCVK9lV1Gg.png)
-Draw a smaller concentric circle for the eye and subtract the smaller circle from the larger shape.
+画一个小一些的同心圆来创建中间的小洞,从较大的图形中减去中心的小圆。
![](https://cdn-images-1.medium.com/max/4000/1*Gtie3FaqFE4zPLpSUZLyiA.png)
-A few more ideas (try different shapes):
+可以尝试更多的创意(尝试不同的形状):
![](https://cdn-images-1.medium.com/max/4000/1*KThz-U48zcTbttXMSAqE8Q.png)
-## Method 5: Intersect
+## 方法 5:交叉贯穿
-For the last method we’ll bring back the **Star Tool**.
+最后一种方法又会用到**星型工具(Star Tool)**。
![](https://cdn-images-1.medium.com/max/3200/1*06OYxdrlLjWHY1_7szWRSQ.gif)
-Draw a star.
+画一个星形。
![](https://cdn-images-1.medium.com/max/4000/1*ptztuAZB-wBoq9DjIpnHtg.png)
-Draw a concentric circle on top.
+在上面画一个同心圆。
![](https://cdn-images-1.medium.com/max/4000/1*Zqu7DDvt0TVfV2rmlYqJLQ.png)
-Select both shapes. **Pathfinder** panel > **Intersect**.
+选择这两个图形。**路径查找器(Pathfinder)** 面板 > **相交(Intersect)**。
![](https://cdn-images-1.medium.com/max/4000/1*GvWwEYCuXHmZ7lzpp0nZnQ.png)
-Draw another concentric circle on top like so:
+再如下图所示,在上面画一个同心圆:
![](https://cdn-images-1.medium.com/max/4000/1*NclTXoSkV7L1ESDlALrGAg.png)
-**Pathfinder** panel > **Unite**. Now we have a silhouette of a gear.
+**路径查找器(Pathfinder)** 面板 > **联合(Unite)**。 现在我们得到一个齿轮的轮廓啦。
![](https://cdn-images-1.medium.com/max/4000/1*9kHuhbQmdzgV6HPRi07jAA.png)
-You know what to do — draw a third concentric circle and subtract the smaller circle from the larger shape.
+接下来怎么做你应该都已经知道了吧 —— 绘制第三个同心圆,然后从较大的形状中减去较小的圆。
![](https://cdn-images-1.medium.com/max/4000/1*5f7m7XfibpX337PGdK2k4A.png)
-More with this method:
+用此方法还可以创造出更多花样:
![](https://cdn-images-1.medium.com/max/4000/1*Sg4yaJDl5jo2CDaNe5BMUw.png)
-## Experiment to Find Your Flow
+## 尝试找到自己的方法
-Hope you learned a trick or two from this exercise. Similar methods may be applied in UI-focused vector software like Sketch or Figma, though Illustrator is more precise.
+希望你能从这个教程中学到一两招。虽然 Illustrator 绘图更精密一些,但文中类似的方法也可以应用于一些 UI 制作的矢量软件中,如 Sketch 或 Figma。
-From here, explore different icon styles.
+从这里,探索些不同的图标风格。
![](https://cdn-images-1.medium.com/max/4000/1*L3xu6HufPN2eJMFwNwWQMQ.png)
-## Bonus
+## 彩蛋
-Some more food for thought…
+更多给你带来灵感的创意。
-#### 2 Squares = 1 Star
+#### 2 个正方形 = 1 个星
-You can create an 8-pointed star by drawing 2 squares. **Shift** + **drag** with the **Rectangle Tool** (**M**) to create a square, select and **shift** + **drag** with the **Rotate Tool** (**R**) to rotate in 45° increments.
+你可以通过画两个正方形来创建一个八角星。按下 **Shift** 键的同时,拖动 **矩形工具(M)** 创建一个正方形,选中这个正方形,按下 **Shift** 键的同时,**拖动旋转工具(R)** 来旋转 45°。
![](https://cdn-images-1.medium.com/max/4000/1*4KOU3XYdLAiaWe33ipWsNg.png)
-#### Roundabout Rounding
+#### 环形圆角
-Once upon a time, I may have hacked rounded corners by adding a stroke — overcomplicated and imprecise. Oof.
+在以前,我可能会通过添加笔触(stroke)来完成圆角,现在看来但这实在是一个复杂又不精确的方法。(真是令人尴尬)
![](https://cdn-images-1.medium.com/max/4000/1*l11n93ZXg-wrMYtKyeh-nQ.png)
-#### Scribble to Shape
+#### 涂鸦的造型
-If you’re using a tablet or touch interface, you can use the wacky **Shaper Tool** (**shift** + **N**) to non-destructively combine or subtract shapes. Scribble over like so to ‘delete’ the desired areas. The original shapes will be preserved.
+如果你使用的是平板电脑或者是触控板,你可以使用古怪的 **Shaper 工具**(**Shift** + **N**)来对图形进行不会被破坏的组合或删减。就像下面这样乱涂乱画来“删除”想要的区域。最后原始的图形会被保留下来。
![](https://cdn-images-1.medium.com/max/4000/1*O-W5KyVHqmUgO4TWnvO0nA.png)
---
-🎶 **Written to the sounds of: [Mogwai](https://open.spotify.com/artist/34UhPkLbtFKRq3nmfFgejG?si=QsV-S2PuTlKKJTzlRF1uDw)**
+🎶 **文章的音频版:[Mogwai](https://open.spotify.com/artist/34UhPkLbtFKRq3nmfFgejG?si=QsV-S2PuTlKKJTzlRF1uDw)**
-🙏 **Thanks to: Toby Fried, Tate Chow, Christine Lee, Pawel Piekarski, and Monica Chang**
+🙏 **感谢:Toby Fried、Tate Chow、Christine Lee、Pawel Piekarski、Monica Chang**
---
-More about iconography:
+更多关于图像设计的文章:
-* [7 Principles of Icon Design](https://medium.com/@minoraxis/7-principles-of-icon-design-e7187539e4a2)
-* Icon Grids & Keylines Demystified **(coming soon)**
-* Pixel-Snapping in Icon Design: To Snap or Not to Snap **(coming soon)**
+* [关于 icon 设计的 7 个准则](https://medium.com/@minoraxis/7-principles-of-icon-design-e7187539e4a2) ([译文连接](https://juejin.im/post/5e5dbd3e6fb9a07cd323dd2b))
+* icon 网格 & 关键线大揭秘 **(即将推出)**
+* icon 设计中的像素捕捉:捕捉或不捕捉 **(即将推出)**
> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
diff --git a/TODO1/custom-encoding-and-decoding-json-in-swift.md b/TODO1/custom-encoding-and-decoding-json-in-swift.md
new file mode 100644
index 00000000000..06a3d11719b
--- /dev/null
+++ b/TODO1/custom-encoding-and-decoding-json-in-swift.md
@@ -0,0 +1,305 @@
+> * 原文地址:[Custom encoding and decoding JSON in Swift](https://levelup.gitconnected.com/custom-encoding-and-decoding-json-in-swift-a99c80b280e7)
+> * 原文作者:[Leandro Fournier](https://medium.com/@lean4nier)
+> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
+> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/custom-encoding-and-decoding-json-in-swift.md](https://github.com/xitu/gold-miner/blob/master/TODO1/custom-encoding-and-decoding-json-in-swift.md)
+> * 译者:
+> * 校对者:
+
+# Custom encoding and decoding JSON in Swift
+
+![](https://cdn-images-1.medium.com/max/2000/0*-t2P3atrbgHMKR6P.jpg)
+
+> This post was originally written by me at [Swift Delivery](https://www.leandrofournier.com/custom-encoding-and-decoding-json/).
+
+In our last [Working with JSON in Swift series](https://levelup.gitconnected.com/working-with-json-in-swift-c5faea0b19a1), we explore various items:
+
+* `Codable` protocol, which contains two other protocols: `Encodable` and `Decodable`
+* How to decode a JSON data object into a readable Swift struct
+* Usage of custom keys
+* Custom objects creation
+* Arrays
+* Different top level entities
+
+That’s enough for a basic usage of JSON in Swift, which will enable us to read JSON data (decode) and create a new object which can be converted back to JSON (encode) and send it, for instance, to a RESTFul API.
+
+So, first things first: let’s create an object and convert it to a JSON data format.
+
+## Encoding
+
+#### Default encoding
+
+Let’s assume we have the following `struct` for insects:
+
+```swift
+struct Insect: Codable {
+ let insectId: Int
+ let name: String
+ let isHelpful: Bool
+
+ enum CodingKeys: String, CodingKey {
+ case insectId = "insect_id"
+ case name
+ case isHelpful = "is_helpful"
+ }
+}
+```
+
+To sum up, we have three properties. **insectId** for the insect identifier, **name** for its name and **isHelpful** to specify if the insect is helpful or not to our garden. Two of these properties use custom keys (**insectId** and **isHelpful**).
+
+Now let’s create an insect:
+
+```swift
+let newInsect = Insect(insectId: 1006, name: "ants", isHelpful: true)
+```
+
+Our RESTful API expects to receive a JSON with this new insect information. Then we have to encode it:
+
+```swift
+let encoder = JSONEncoder()
+let insectData: Data? = try? encoder.encode(newInsect)
+```
+
+That was easy: now **insectData** is an object of type `Data?`. We might want to check whether the encoding actually worked (just a check, you probably won't do this in your code). Let's rewrite the code above and use some optional unwrapping:
+
+```swift
+let encoder = JSONEncoder()
+if let insectData = try? encoder.encode(newInsect),
+ let jsonString = String(data: insectData, encoding: .utf8)
+ {
+ print(jsonString)
+}
+```
+
+1. Create the encoder
+2. Try to encode the object we’ve created
+3. Convert, if possible, the `Data` object into a `String`
+
+Then we print out the result which will look like this:
+
+```json
+{"name":"ants","is_helpful":true,"insect_id":1006}
+```
+
+> Note that the keys used while encoding are not the custom keys (**insectId** and **isHelpful**) but the expected keys (**insect_id** and **is_helpful**). Nice!
+
+#### Custom encoding
+
+Let’s suppose our RESTful API expects to receive the name of the insect uppercased. We need to create our own implementation of the encoding method so to make sure the name of the insect is sent uppercased. We must implement the method **func** encode(to encoder: Encoder) **throws** of the `Encodable` protocol inside our **Insect** `struct`.
+
+```swift
+struct Insect: Codable {
+ let insectId: Int
+ let name: String
+ let isHelpful: Bool
+
+ enum CodingKeys: String, CodingKey {
+ case insectId = "insect_id"
+ case name
+ case isHelpful = "is_helpful"
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(insectId, forKey: .insectId)
+ try container.encode(name.uppercased(), forKey: .name)
+ try container.encode(isHelpful, forKey: .isHelpful)
+ }
+}
+```
+
+Line 13 is where we create a container where encoded values will be stored. The container MUST be a `var` because it's mutable and MUST receive the keys to be used.
+
+Lines 14 to 16 are used to encode the values into the container, which is done using `try` because any of these can throw an error.
+
+Now, look at line 15: we don’t just put the value as it is but we uppercase it, which is main reason we’re implementing a custom encoding.
+
+If you run the code above, where we create the **Insect** “ants”, we’ll see that after encoding and converting the resulting JSON `Data` into a `String` we get the following:
+
+```
+{"name":"ANTS","is_helpful":true,"insect_id":1006}
+```
+
+As you might have seen, the name of the insect is now uppercased despite we’ve created it lowercased. How cool is that!
+
+## Custom decoding
+
+So far we’ve been relying on the default decoding method of the `Decodable` protocol. But let's take a look at another scenario.
+
+```json
+[
+ {
+ "insect_id":1001,
+ "name":"BEES",
+ "details":{
+ "is_helpful":true
+ }
+ },
+ {
+ "insect_id":1002,
+ "name":"LADYBUGS",
+ "details":{
+ "is_helpful":true
+ }
+ },
+ {
+ "insect_id":1003,
+ "name":"SPIDERS",
+ "details":{
+ "is_helpful":true
+ }
+ },
+ {
+ "insect_id":2001,
+ "name":"TOMATO HORN WORMS",
+ "details":{
+ "is_helpful":false
+ }
+ },
+ {
+ "insect_id":2002,
+ "name":"CABBAGE WORMS",
+ "details":{
+ "is_helpful":false
+ }
+ },
+ {
+ "insect_id":2003,
+ "name":"CABBAGE MOTHS",
+ "details":{
+ "is_helpful":false
+ }
+ }
+]
+```
+
+The API is retrieving the **is_helpful** property inside a **details** entity. But we don’t want to create a **Details** object: we just want to flatten that object so we can use our existing **Insect** object.
+
+Time to use our own implementation of the **init**(from decoder: Decoder) **throws** method of the `Decodable` protocol and some extra work. Let's get started.
+
+First of all, coding keys has changed because **is_helpful** is not a key in the same level as before AND there’s a new one called **details**. To fix that:
+
+```swift
+enum CodingKeys: String, CodingKey {
+ case insectId = "insect_id"
+ case name
+ case details
+ }
+
+ enum DetailsCodingKeys: String, CodingKey {
+ case isHelpful = "is_helpful"
+ }
+```
+
+In line 4 we replace the existing key with the new one, **details**.
+
+In lines 7 and 9 we create a new set of keys, the ones that exist inside **details**, which in this case is just one, **isHelpful**.
+
+> Note that we didn’t touch the properties of the **Insect** `struct`.
+
+Now let’s dive into the decoder initialization:
+
+```swift
+init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+
+ insectId = try container.decode(Int.self, forKey: .insectId)
+ name = try container.decode(String.self, forKey: .name)
+ let details = try container.nestedContainer(keyedBy: DetailsCodingKeys.self, forKey: .details)
+ isHelpful = try details.decode(Bool.self, forKey: .isHelpful)
+}
+```
+
+In line 2 we create the container which decodes the whole JSON structure.
+
+In line 4 and 5 we just decode the `Int` value for the **insectId** property and the `String` value for the **name** property.
+
+In line 6 we grab the nested container under the **details** key which is keyed by the brand new **DetailsCodingKeys** `enum`.
+
+In line 7 we just decode the `Bool` value for the **isHelpful** property inside our new **details** container.
+
+BUT that’s not it. Since our **CodingKeys** has changed by adding the **details** case, our custom encoding implementation must be fixed. So let’s do that:
+
+```swift
+func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(insectId, forKey: .insectId)
+ try container.encode(name.uppercased(), forKey: .name)
+ var details = container.nestedContainer(keyedBy: DetailsCodingKeys.self, forKey: .details)
+ try details.encode(isHelpful, forKey: .isHelpful)
+}
+```
+
+We just changed the way we encode the **isHelpful** property.
+
+In line 5 we create a new nested container, using the keys inside the **DetailsCodingKeys** `enum` and to be used inside the **details** entity inside our JSON.
+
+In line 6 we encode **isHelpful** INSIDE the brand new **details** nested container.
+
+So, to sum up, our **Insect** `struct` looks like this:
+
+```swift
+struct Insect: Codable {
+ let insectId: Int
+ let name: String
+ let isHelpful: Bool
+
+ enum CodingKeys: String, CodingKey {
+ case insectId = "insect_id"
+ case name
+ case details
+ }
+
+ enum DetailsCodingKeys: String, CodingKey {
+ case isHelpful = "is_helpful"
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+
+ insectId = try container.decode(Int.self, forKey: .insectId)
+ name = try container.decode(String.self, forKey: .name)
+ let details = try container.nestedContainer(keyedBy: DetailsCodingKeys.self, forKey: .details)
+ isHelpful = try details.decode(Bool.self, forKey: .isHelpful)
+
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(insectId, forKey: .insectId)
+ try container.encode(name.uppercased(), forKey: .name)
+ var details = container.nestedContainer(keyedBy: DetailsCodingKeys.self, forKey: .details)
+ try details.encode(isHelpful, forKey: .isHelpful)
+ }
+}
+```
+
+If we decode it:
+
+```swift
+let decoder = JSONDecoder()
+if let insects = try? decoder.decode([Insect].self, from: jsonData!) {
+ print(insects)
+}
+```
+
+We’ll get something like this:
+
+```
+[__lldb_expr_54.Insect(insectId: 1001, name: "BEES", isHelpful: true), __lldb_expr_54.Insect(insectId: 1002, name: "LADYBUGS", isHelpful: true), __lldb_expr_54.Insect(insectId: 1003, name: "SPIDERS", isHelpful: true), __lldb_expr_54.Insect(insectId: 2001, name: "TOMATO HORN WORMS", isHelpful: false), __lldb_expr_54.Insect(insectId: 2002, name: "CABBAGE WORMS", isHelpful: false), __lldb_expr_54.Insect(insectId: 2003, name: "CABBAGE MOTHS", isHelpful: false)]
+```
+
+As you can see, there’s no **details** entity: just our `struct` properties.
+
+The encoding will work as expected as well.
+
+This post, plus the previous series, sums up pretty much the most common scenarios you may run into when working with JSON in Swift.
+
+## Need more information?
+
+Ben Scheirman’s [Ultimate Guide to JSON Parsing with Swift](https://benscheirman.com/2017/06/swift-json/) is the most useful source I could find for this subject.
+
+> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
+
+---
+
+> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。
diff --git a/TODO1/fast-pipelines-with-generators-in-typescript.md b/TODO1/fast-pipelines-with-generators-in-typescript.md
index 513ae294fed..68e447d7b79 100644
--- a/TODO1/fast-pipelines-with-generators-in-typescript.md
+++ b/TODO1/fast-pipelines-with-generators-in-typescript.md
@@ -2,14 +2,14 @@
> * 原文作者:[Wim Jongeneel](https://medium.com/@wim.jongeneel1)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/fast-pipelines-with-generators-in-typescript.md](https://github.com/xitu/gold-miner/blob/master/TODO1/fast-pipelines-with-generators-in-typescript.md)
-> * 译者:
-> * 校对者:
+> * 译者:[febrainqu](https://github.com/febrainqu)
+> * 校对者:[xionglong58](https://github.com/xionglong58),[GJXAIOU](https://github.com/GJXAIOU),[lsvih](https://github.com/lsvih)
-# Lazy Pipelines with Generators in TypeScript
+# TypeScript 中带生成器的惰性管道
![Photo by [Quinten de Graaf](https://unsplash.com/@quinten149?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)](https://cdn-images-1.medium.com/max/9704/1*wEQnHaPoHc_QJo5vxwrCEg.jpeg)
-In recent years the JavaScript community has embraced the functional array methods like `map` and `filter`. Writing for-loops has become something that gets associated with 2015 and JQuery. But the array methods in JavaScript are far from ideal when we are talking about performance. Lets look at an example to clarify the issues:
+近年来,JavaScript 社区已经接受了 `map` 和 `filter` 之类的函数式数组方法,for 循环成为了只能在 Jquery 中见到的东西。但在性能方面,JavaScript 中的数组方法还远远达不到预期。让我们看一个例子:
```TypeScript
const x = [1,2,3,4,5]
@@ -18,24 +18,24 @@ const x = [1,2,3,4,5]
[0]
```
-This code will execute the following steps:
+这段代码将执行以下步骤:
-* create the array with 5 items
-* create a new array with all the numbers doubled
-* create a new array with the numbers filtered
-* take the first item
+* 创建一个含有五个元素的数组
+* 创建一个新数组,其元素值是前一个数组对应元素的 2 倍
+* 创建一个符合过滤条件的新数组
+* 取数组的第一个元素
-This involves a lot more stuff happening then is actually needed. The only thing has to happen is that the first item that passes `x > 5` gets processed and returned. In other languages (like Python) iterators are used to solve this issue. Those iterators are a lazy collection and only processes data when it is requested. If JavaScript would use lazy iterators for its array methods the following would happen instead:
+实际上有很步骤是多余的,上述代码做的唯一的事就是返回第一个大于 5 的元素。在其他语言中(例如 Python)可以用迭代器来解决此类问题。这些迭代器是一个惰性集合,只在请求时处理数据。如果用 JavaScript 的惰性迭代器代替上面的一系列数组方法,则需要进行如下步骤:
-* `[0]` requests the first item from `filter`
-* `filter` requests items from `map` until it has found one item that passes the predicate and yields (‘returns’) it
-* `map` has processed an item for each time `filter` requested it
+* `[0]` 请求经 `filter` 操作后数组的第一个元素
+* `filter` 从 `map` 中请求元素,直到发现一个符合条件的元素,并返回(‘yield’)它
+* 每当 `filter` 发送一次请求,`map` 便处理一个元素
-Here we did only `map` and `filter` the first tree items in the array because no more items where requested from the iterator. There where also no additional arrays or iterators constructed because every item goes through the entire pipeline one after the other. This is a concept that **can** result in massive performance gains when processing a lot of data.
+在本例中,我们只对数组中的第一项进行了 `map` 和 `filter` 操作,接着迭代器就不会再请求其它项。这样也不需要另外构建数组或迭代器,因为每一项都是一步接一步地完成整个管道。因此,**惰性管道**这个概念**可以**在处理大量数据时获得巨大的性能收益。
-## Generators and iterators in JavaScript
+## JavaScript 中的生成器和迭代器
-Luckily for us JavaScript does actually support the concept of [iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators). They can be created with [generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) functions that yield the items of the collection. A generator function looks as follows:
+幸运的是 JavaScript 确实支持[迭代器](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)的概念。可以使用[生成器](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)函数来创建集合中的各个元素。一个生成器函数如下:
```TypeScript
function* iterator() {
@@ -49,7 +49,7 @@ for(let x of iterator()) {
}
```
-Here the for-loop will request an item of the iterator for each loop. The generator function uses the `yield` keyword to return the next item in the collection. As you see we can yield multiple times to create iterators that contain multiple items. There will never be any array constructed in memory. We can make this a bit better to understand when we remove some of the syntax sugar:
+在这里,for 循环将在每次循环中请求迭代器的一个元素。生成器函数使用 `yield` 关键字来返回集合中的下一项。如你所见,我们可以多次生成包含多个项的迭代器,因此永远不需要在内存中构造额外的数组。我们可以删除一些语法糖方便理解:
```TypeScript
const itt = iterator()
@@ -61,17 +61,17 @@ while(current.done == false) {
}
```
-Here you can see that an iterator has a `next` method for requesting the next item. The result of this method has the value and a boolean indicating of we have more results left in the iterator. While this all very interesting, we will need some more things if we want to construct proper data pipelines with iterators:
+你可以看到迭代器有一个 `next` 方法用于请求下一项,此方法的将返回一个值和一个布尔值,布尔值用于指示迭代器中是否还有更多结果。虽然这一切都很有趣,但如果我们想要使用迭代器构建正确的数据管道,还需要做更多的事情:
-* conversions from arrays to iterators and back
-* iterators that operate on other iterators, like `map` and `filter` (also called ‘higher-order iterators’)
-* a proper interface to chain all of those together in an elegant and practical way
+* 从数组到迭代器的转换
+* 在其它迭代器上运作的迭代器,如 `map` 和 `filter`(也称为“高阶迭代器”)
+* 一个合适的接口,以优雅和实用的方式将所有步骤链接在一起
-In the rest of this article I will show how to do those things. At the end I have included a link to a library I created that contains a lot more features. Sadly, this is not a native implementations of lazy iterators. This means that there is overhead and in a lot of cases this library is not worth it. But I still want to show you the concept in action and discuss its pros and cons.
+下面,我将展示如何实现这些功能。在文末我留了一个链接,指向我创建的有着更多功能的库。遗憾的是,这不是惰性迭代器的原生实现,这也意味着用这个库存在额外开销,而且导致在一些情况下不值得用它。但我还是想向你们展示这个概念的实际应用,并讨论它的利弊。
-## Iterator constructors
+## 迭代器的构造函数
-We want to be able to create iterators from multiple data sources. The most oblivious one is arrays. This one is quite easy, we loop over the array and yield all items:
+我们希望能够从多个数据源创建迭代器。最容易被遗忘的就是数组。这是相当容易的,我们循环数组,并产生所有项目:
```TypeScript
function* from_array(a:a[]) {
@@ -79,7 +79,7 @@ function* from_array(a:a[]) {
}
```
-Turning an iterator in an array will require us to call `next` until we have gotten all the items. After this we can return our array. Of course you only want to turn an iterator into an array when absolutely needed because this function causes a full iteration.
+在数组中可以用 `next` 调用迭代器,直到获得所有的数组元素。当然希望你只在别无选择时再将迭代器转换回数组,因为这个函数需要进行一次完整的迭代:
```TypeScript
function to_array(a: Iterator) {
@@ -93,7 +93,7 @@ function to_array(a: Iterator) {
}
```
-Another method for reading data from an iterator is `first`. Its implementation is shown bellow. Note that it only request the first item from the iterator! This means that all the following potential values will never be calculated, resulting in less waste of resources in the data pipeline.
+从迭代器中读取数据的另一种方法是 `first`,它的实现如下所示。注意,它只向迭代器请求第一项,这也意味着剩下的值将永远不会被计算到,从而减少数据管道中的资源浪费。
```TypeScript
export function first(a: Iterator) {
@@ -101,11 +101,11 @@ export function first(a: Iterator) {
}
```
-In the complete library there are also constructors that create iterators from [functions](https://github.com/WimJongeneel/ts-lazy-collections/blob/master/src/main.ts#L65-L74) or [ranges](https://github.com/WimJongeneel/ts-lazy-collections/blob/master/src/main.ts#L57-L63).
+在完整的库中还有一些构造函数,它们会从 [functions](https://github.com/WimJongeneel/ts-lazy-collections/blob/master/src/main.ts#L65-L74) 或 [ranges](https://github.com/WimJongeneel/ts-lazy-collections/blob/master/src/main.ts#L57-L63) 创建迭代器。
-## Higher-order iterators
+## 高阶迭代器
-A higher-order iterator transforms an existing iterator into a new iterator. Those iterators are what makes up the operations in a pipeline. The well-known transform function `map` is shown bellow. It takes an iterator and a function and returns a new iterator where the function is applied to all items in the original iterator. Note that we still yield item-for-item and preserve the lazy nature of the iterators while transforming them. This is very important if we want to actually achieve the higher efficiency I talked about in the intro of this article!
+高阶迭代器会将现有的迭代器转换为新的迭代器,这些迭代器组成了管道中的操作。著名的转换函数 `map` 如下所示。它接受一个迭代器和一个函数,并返回一个新的迭代器,其中该函数应用于原始迭代器中的所有项。请注意,我们仍然会一项一项地生成(yield),并在转换迭代器时保留迭代器的惰性性质,这也是实现这篇文中所说的“更高效率”的关键点。
```TypeScript
function* map(a: Iterator, f:(a:a) => b){
@@ -117,7 +117,7 @@ function* map(a: Iterator, f:(a:a) => b){
}
```
-Filter can be implemented in a similar way. When requested for the next item, it will keep requesting items from its inner iterator until it has found one that passed the predicate. This item will be yielded and execution is halted until the request for the next item comes in.
+过滤器可以用类似的方式实现。当请求下一项时,它将一直从内部迭代器请求元素,直到找到一个通过条件的元素,生成(yield)此项,并停止执行迭代,直到收到生成下一个元素的请求。
```TypeScript
function* filter(a: Iterator, p: (a:a) => boolean) {
@@ -129,11 +129,11 @@ function* filter(a: Iterator, p: (a:a) => boolean) {
}
```
-Many more higher-order iterators can be constructed in with the same concepts I have show above. The complete library ships with a lot of them, check them out over [here](https://github.com/WimJongeneel/ts-lazy-collections#collection-methods).
+可以用上面介绍的概念构造更多的高阶迭代器。[完整的库](https://github.com/WimJongeneel/ts-lazy-collections#collection-methods)中有更多种类的高阶迭代器用于参考,欢迎访问。
-## The builder interface
+## 构建器接口
-The last part of the library is the public facing API. The library uses the builder pattern to allow you to chain methods like on arrays. This is done by creating a function that takes an iterator and returns an object with the methods on it. Those methods can call the constructor again with an updated iterator for the chaining:
+库的最后一部分是面向用户的 API。该库使用了构建器模式,来让你像在数组上那样进行链式调用。这是通过创建一个接受迭代器,并返回带有方法的对象的函数来完成的。这些方法可以再次调用构造函数与更新迭代器的链接:
```TypeScript
const fromIterator = (itt: Iterator) => ({
@@ -144,7 +144,7 @@ const fromIterator = (itt: Iterator) => ({
})
```
-The example of the start of this article can be written as bellow. In this implementation we don’t create additional arrays and only process the data that is actually used!
+本文开头的例子可以写成如下形式。在这个实现中,我们不再需要创建额外的数组,只需要处理实际使用的数据!
```TypeScript
const x = fromIterator(from_array([1,2,3,4,5]))
@@ -153,11 +153,11 @@ const x = fromIterator(from_array([1,2,3,4,5]))
.first()
```
-## Conclusion
+## 结论
-In this article I have shown you how generators and iterators can be used to create a powerful and very efficient library for processing lots of data. Of course iterators are not the golden bullet that will fix everything. The gains in efficiency are down to saving on unnecessary calculations. How much this means in real numbers is completely down to how much calculations there can be optimized out, how heavy those calculations are and how much data you are processing. When there are no calculations to save or the collections are relative small, you will potentially lose performance to the overhead of the library.
+在本文中,我向您展示了如何使用生成器和迭代器来创建功能强大且非常高效的库来处理大量数据。当然,迭代器并不是解决所有问题的金钥匙。效率的提高是由于节省了不必要的计算。实际上提升了多少,完全取决于可以优化多少计算、这些计算有多繁重以及要处理多少数据。当没有要保存的计算或集合相对较小时,你可能会因为库的开销而损失性能。
-The full source code can be found on [Github](https://github.com/WimJongeneel/ts-lazy-collections#collection-methods) and contains more features that fitted in this article. I would love to hear your opinion on this. Do you think it is a pity that JavaScript doesn’t use lazy iteration for the array methods? And do you think that using generators is the way forward for collections in JavaScript? If JavaScript would use lazy iterators by default they should be able to optimize the overhead away (like other languages have done) while still preserving the potential wins with efficiency.
+完整的源代码可以在 [Github](https://github.com/WimJongeneel/ts-lazy-collections#collection-methods) 找到并包含本文中包含的更多特性。我很想听听你对此的意见。你是否认为 JavaScript 不对数组方法使用惰性迭代是很可惜的?你是否认为使用生成器是 JavaScript 集合的前进方向?如果JavaScript在默认情况下使用惰性迭代器,则它们应该能够优化开销(就像其他语言一样),同时仍然保持效率的潜在优势。
> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
diff --git a/TODO1/how-to-be-a-good-remote-developer.md b/TODO1/how-to-be-a-good-remote-developer.md
index 9759e097a5f..ecee1e771e0 100644
--- a/TODO1/how-to-be-a-good-remote-developer.md
+++ b/TODO1/how-to-be-a-good-remote-developer.md
@@ -2,90 +2,90 @@
> * 原文作者:[John Au-Yeung](https://medium.com/@hohanga)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-be-a-good-remote-developer.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-be-a-good-remote-developer.md)
-> * 译者:
-> * 校对者:
+> * 译者:[Badd](https://juejin.im/user/5b0f6d4b6fb9a009e405dda1)
+> * 校对者:[zhanght9527](https://github.com/zhanght9527), [QinRoc](https://github.com/QinRoc)
-# How to Be a Good Remote Developer
+# 如何成为一名优秀的远程开发者
-![Photo by [Nicole Wolf](https://unsplash.com/@joeel56?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/12000/0*gcJQWqRaHf8E66Bw)
+![图片来自 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 的 [Nicole Wolf](https://unsplash.com/@joeel56?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/12000/0*gcJQWqRaHf8E66Bw)
-Being a good remote worker takes a lot of discipline. We have to do everything ourselves without anyone watching.
+要成为一名优秀的远程办公人士,需要极强的自制力。我们需要在无人监督的环境中独自完成每一项工作。
-In this article, we’ll look at some habits we can adopt to become a good remote developer.
+在本文中,我们将介绍一些可以帮我们成为优秀远程开发者的习惯。
-## Practice Good Meeting Etiquette
+## 养成良好的会议礼仪
-Good meeting etiquette is very important. Since we're starting at our screen for meetings and don’t have to go to a physical meeting room, it’s all on us to be a practice good meeting etiquette.
+良好的会议礼仪极其重要。由于我们只需要在屏幕前参加会议,不用进入真实的会议室,所以想要养成良好的会议礼仪,全凭自我约束。
-We should have everyone on separate screens. Our face should be on video if we’re participating.
+我们应该把每个人的图像都放在单独的窗口里。当我们参与讨论时,应保持脸部出现在视频画面里。
-Also, we have to pay attention and don’t get distracted by random things around us.
+同时,我们必须全神贯注,不能被身边琐事干扰。
-If we don’t have anything urgent, then we should do other things.
+除非有其他紧急事情,否则的话,有这么几件事是我们应该做的。
-Being on the computer, it’s easy to take notes, so we should do it.
+应该保持人在电脑旁,这样方便记笔记。
-Also, we should probably wear work attire if we turn on video so that we won’t embarrass ourselves if we stand up and have underwear on.
+另外,如果开启了摄像头,我们应该着装端正,这样就不会出现起身时内衣意外上镜的尴尬场面。
-## Experiment With What Makes Us the Most Productive
+## 尝试能让我们以最高效率产出的方式
-Since we aren’t using only office equipment and staying in the office, we can experiment with our home office setup to see what works for us.
+既然我们不仅仅是使用办公设备,更是生活在办公空间中,我们尽可以尝试不一样的家庭办公方式,看看哪些适合我们。
-We can wear whatever makes us comfortable as long it’s not underwear or any non-business clothing.
+除了内衣和其他非正式的着装,我们大可以让自己穿得舒服一些。
-Also, we can shift our start and end times a bit as long we don’t miss any meetings.
+我们还可以稍稍地调整作息时间,只要不错过会议就行。
-The type of work that we do during the day can also change according to our schedule since no one is watching and micromanaging what we’re doing.
+既然没人时刻监督和管理我们做什么,我们也可以根据日程调整当日的工作类型。
-We can listen to whatever we want and sit anywhere that makes us more comfortable.
+我们可以听任何我们自己喜欢的音乐,可以坐在任何我们觉得更舒服的地方。
-Also, we probably don’t have any set lunch hour. That’s nice since that means we can adjust our lunch schedule the way we wish.
+另外,我们的午餐时间可以不固定。我们可以想什么时候吃就什么时候吃,太棒了。
-## Clear Communication
+## 清晰的沟通
-Since we don’t see each other in person, we can’t read people’s body language to get what they mean when they type a message or talk.
+由于我们无法和其他人当面沟通,所以当通过打字或者语音电话进行沟通时,我们无法通过他们的肢体语言来理解他们的意思。
-Therefore, we should make sure that everything is clear and that people understand what we want to convey.
+因此,我们应该确保把所有细节说清楚,以便于他人理解我们想传达的内容。
-Dealing with different time zones and different mediums of communication also makes things harder for us.
+时区和沟通媒介的差异让远程沟通难上加难。
-Therefore, to make everyone’s lives easier, we should outline our ideas clearly and type out actionable steps.
+所以,为了帮他人降低理解难度,我们应该清晰地描述我们的想法并给出可执行的步骤。
-Meeting notes are more important since we aren’t sitting beside each other to ask questions.
+由于我们并不是坐在一起提出问题,会议记录的作用显得更加重要。
-Instead of using a real whiteboard, we have to use a virtual one.
+没有真实的白板,我们就用白板软件。
-If we have to pair program, we have to talk and share our screens.
+如果我们需要结对编程,我们应该开启语音通话并共享屏幕。
-Also, no one will know how we feel over the Internet, so we should convey those clearly.
+另外,在网上,没人能感知我们的感受,所以我们应该把自己的感受清晰地传达出来。
-If we need help, we’ve to ask around since not everyone may answer.
+当我们需要帮助,我们需要多方求助,因为不是每个人都能为我们解答。
-Also, we should check our notifications so that we’ll answer people when we see them. People are waiting for our answers and this is more important since no one is around us to answer urgent questions if we need to.
+还有,我们应该经常检查消息通知,以便及时回复他人。别人在等我们的回复呢,当我们急于寻求答复但周围没有人能响应时,我们就更能体会这一点的重要性。
-![Photo by [Annie Spratt](https://unsplash.com/@anniespratt?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/15904/0*jlbf4_s4q88Oz-IR)
+![图片来自 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 的 [Annie Spratt](https://unsplash.com/@anniespratt?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/15904/0*jlbf4_s4q88Oz-IR)
-## Create Boundaries Between Work and Life
+## 划清工作和生活的边界
-If we’re working 9 to 5 at the office, then we got to set similar boundaries even if we’re working at the office.
+如果我们在办公室时的作息时间是朝九晚五,那么在家办公时,也应该设置相近的时间点。
-There’s just no way that we can work 24 hours a day and not burn out and be tired.
+我们不可能一天工作 24 小时而不感到疲惫。
-Therefore, we should set some boundaries so that we cut off work the same way that we go home at the end of the day.
+所以,我们应该设置些许边界来中止工作,就像从办公室里下班回家那样。
-At the end of the day, turn off work notifications, close all our work stuff and relax. Also, we should take breaks like we do when we’re in the office.
+当一天结束时,关闭工作消息通知,收起所有工作相关事物,好好休息吧。另外,我们应该像在办公室里那样,适时休息、劳逸结合。
-We got to walk around and clear our minds. Also, lunch breaks are equally important, so we should have them.
+我们得四处走走、放空头脑。而且,午休也相当重要,所以也应该午休。
-## Conclusion
+## 总结
-Even though we’re working remotely, we got to set our boundaries like we do when we’re working at the office.
+即使我们是在远程办公,我们也应该像在办公室上班那样,建立工作和生活的边界。
-The difference is that we have to make sure that we check our notifications more frequently during work hours so that we can answer people’s questions.
+区别在于,我们要确保在工作时间内,更加频繁地查看消息通知,以便及时回复他人。
-Also, we should make sure that we convey everything clearly so that we can be sure that everyone understands us.
+另外,我们应该确保清晰地传达意图,让他人准确领会我们的想法。
-Finally, to collaborate, we have to use share screens and use video conference software.
+最后,我们要通过共享屏幕和视频会议软件来协同办公。
> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。
diff --git a/TODO1/image-manipulation-libraries-for-javascript.md b/TODO1/image-manipulation-libraries-for-javascript.md
new file mode 100644
index 00000000000..e9cb2bc3a8c
--- /dev/null
+++ b/TODO1/image-manipulation-libraries-for-javascript.md
@@ -0,0 +1,181 @@
+> * 原文地址:[10 JavaScript Image Manipulation Libraries for 2020](https://blog.bitsrc.io/image-manipulation-libraries-for-javascript-187fde1ad5af)
+> * 原文作者:[Mahdhi Rezvi](https://medium.com/@mahdhirezvi)
+> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
+> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/image-manipulation-libraries-for-javascript.md](https://github.com/xitu/gold-miner/blob/master/TODO1/image-manipulation-libraries-for-javascript.md)
+> * 译者:
+> * 校对者:
+
+# 10 JavaScript Image Manipulation Libraries for 2020
+
+![](https://cdn-images-1.medium.com/max/2560/1*lXwMUm79vvrK_ZjazwqcbA.jpeg)
+
+Working with images in JavaScript can be quite difficult and cumbersome. Thankfully, there are a number of libraries that can make things a lot easier. Below are my favorite ones in different categories.
+
+If you’ve found something useful, try to wrap it as a component of your framework of choice. This way, you’d have a reusable component with a declarative API, always ready to be used.
+
+## 1. Pica
+
+This plugin helps you reduce upload size for large images thereby saving upload time. It allows you to resize images in your browser without pixelation and reasonably fast. It autoselects the best of available technologies out of web-workers, web assembly, createImageBitmap and pure JS.
+
+[Demo](http://nodeca.github.io/pica/demo/)
+[Github](https://github.com/nodeca/pica)
+
+![](https://cdn-images-1.medium.com/max/2086/1*01gc8wM7mYZxRvzM592r-A.png)
+
+---
+
+## 2. Lena.js
+
+This cool image library is very tiny in size but has around 22 image filters that are pretty cool to play around with. You can also create and add new filters to the Github repo as well.
+
+[Demo](https://fellipe.com/demos/lena-js/)
+[Tutorial](https://ourcodeworld.com/articles/read/515/how-to-add-image-filters-photo-effects-to-images-in-the-browser-with-javascript-using-lena-js)
+[Github](https://github.com/davidsonfellipe/lena.js)
+
+![](https://cdn-images-1.medium.com/max/2718/1*rLKUyfeo_LUvvcRr7cYN0Q.png)
+
+---
+
+## 3. Compressor.js
+
+This is a simple JS image compressor that uses the Browser’s native [canvas.toBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) API to handle the image compression. This allows you to set the compression output quality ranging from 0 to 1.
+
+[Demo](https://fengyuanchen.github.io/compressorjs/)
+[Github](https://github.com/fengyuanchen/compressorjs)
+
+![](https://cdn-images-1.medium.com/max/2334/1*hp85KWNmfPftt0MFj_qtEA.png)
+
+---
+
+## 4. Fabric.js
+
+Fabric.js allows you to easily create simple shapes like rectangles, circles, triangles and other polygons or more complex shapes made up of many paths, onto the HTML `\