Node.js 葵花寶典

bigDreamer發表於2019-04-04

Node.js葵花寶典

前言

​ 欲練此功,必先自宮;

​ 不必自宮,亦可練成;

​ 兄臺還是好好修煉此功吧!保持一個清醒的頭腦,你將駕馭這匹野馬!!!

—— 致讀者

​ 知識就像海洋一樣,永遠也學不完,但是不斷精益求精是一種態度,是對新事物的一種持續保持瞻望的態度,但願你在學習的樂園裡不斷完善己身,不斷修煉,等待破繭成蝶。

文件風格

  • 書名採用 #
  • 大標題採用##
  • 單元小結標題採用####

​ —— 致開發者

​ 好的書寫風格能夠讓讀者的思路清晰,同樣能讓人有繼續閱讀的興趣,但願你能按照此風格繼續完善本書籍。

第一篇 為何偏愛Node.js

1.1 引子

  • 前端職責範圍變大,為了統一流程開發體驗
  • 在處理高併發,I/O密集型場景效能優勢足夠明顯

1.2 CPU密集 ==VS== IO密集

  • CPU密集:壓縮、解壓、加密、解密
  • I/O密集:檔案操作、網路操作、資料庫

1.3 Web常見的場景

  • 靜態資源獲取
  • ......

1.4 高併發對應之道

  • 增加物理機的個數
  • 增加每臺機器的CPU數------多核

1.5 關於程式執行緒那些事兒

  • 程式:用一句比較接地氣的一句話敘述就是,執行中的程式就叫做程式。
  • 多程式:啟動多個程式處理一個任務。
  • 執行緒:程式內一個相對獨立、可以排程的執行單元,與同屬一個程式的執行緒共享程式資源。

1.6 再來談談Node.js的單執行緒

  • 單執行緒只是針對主程式,I/O作業系統底層進行多執行緒排程。也就是它僅僅起一個監聽作用。

    ###  舉個例子叭
         場景:飯店
         情節:人流量高併發
         BOSS的策略:僱傭多名廚師,只僱傭一個服務員,當多名顧客點餐的時候,服務員告訴廚師,做菜,上菜。
    複製程式碼
  • 單執行緒並不是只開一個程式。

    ###  Node.js中的cluster(叢集)模組
         官方介紹:單個 Node.js 例項執行在單個執行緒中。 為了充分利用多核系統,有時需要啟用一組 Node.js 程式去處理負載任務。
         Demo:
    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      console.log(`主程式 ${process.pid} 正在執行`);
    
      // 衍生工作程式。
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`工作程式 ${worker.process.pid} 已退出`);
      });
    } else {
      // 工作程式可以共享任何 TCP 連線。
      // 在本例子中,共享的是 HTTP 伺服器。
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end('你好世界\n');
      }).listen(8000);
    
      console.log(`工作程式 ${process.pid} 已啟動`);
    }
    複製程式碼
  • 友情連結Cluster

1.7 Node.js的常用場景

  • Web server
  • 原生程式碼構建

第二篇 環境&除錯

2.1 環境安裝

2.2 環境必須

  • CommonJS

    • 每個檔案都是一個模組,有自己的作用域。(它會自動包裹函式)

    • 在模組內部module變數代表模組本身。

    • module.exports屬性代表模組對外介面。

      ModuleDemo.js

      console.log('this is a module');
      
      const testVar = 100;
      
      function test () {
          console.log(testVar);
      }
      
      module.exports.testVar = testVar;
      module.exports.testFn = test;
      複製程式碼
    • 使用模組之require規則

    ​ - /表示絕對路徑,./表示相對於當前檔案的路徑。

    ​ - 支援jsjsonnode擴充名

    ​ - 不寫路徑則認為是build-in模組或者各級node-modules內的第三方模組

    CommonJS Use Module

    const modu = require('ModuleDemo.js');
    
    console.log(modu.testVar);
    
    console.log(modu.test);
    複製程式碼
    • require特性
      • module被載入的時候執行,載入後快取。(後邊這一句的意義就是,只載入一次,載入完快取)【注:可以做一個小test,在一個test檔案中,同時載入兩次模組,你就會發現其中的奧祕了。】
      • 一旦出現某個模組被迴圈載入,就只會輸出已經執行的部分,還未執行的部分不會輸出。
  • Global

  • Process

2.3 Node.js引用系統模組與第三方模組

  • 引用系統模組

    const fs = require('fs');
    
    const result = fs.readFile('fs.js',( err, data) => {
        if (err) {
            return err;
        }
        
        console.log(data);
    });
    
    console.log(result);
    
    複製程式碼
  • 引用第三方模組

    npm i chalk -S
    複製程式碼
    const chalk = require('chalk');
    複製程式碼

2.4 exports與module.exports

{
    function(exports,require,module,__filename,__dirname) {
        // code
    }
}
複製程式碼
  • 簡而言之,exports就是module.exports的一個簡寫,也就是一個引用,別名。

    exports = {
        a: 1,
        b: 2,
        test: 123
    }
    //這樣是錯誤的
    exports.test = 100//只能新增屬性,但是不能修改其指向
    
    //當然
    module.exports = {
        a:1,
        b:2,
        test:123,
    }
    //這樣是沒問題的
    複製程式碼

2.5 Global物件

  • CommonJS
  • Buffer、process、console
  • timer
global.test = 200;
複製程式碼

2.6 process模組

/**
 *  argv
 *  argv0 
 *  execArgv
 *  execPath
 */

const {argv, argv0, execArgv, execPath} = require('process');

argv.forEach( item => {
    console.log(item);
})

//列印當前工作程式的路徑

console.log(process.cwd());

//setImmediate(fn),不需要任何時間引數,執行最慢
//process.nextTick(fn)
//二者的區別就是後者的執行會先於前者



複製程式碼
  • 簡單說明一下,就是process.nextTick()會把任務放在當前事件迴圈佇列的隊尾,而setImmediate()會把任務放在下一個佇列的隊首,而setTimeout()會把任務放在它倆中間。

2.7 Debug

第三篇 Node.js-API

3.1 path

和路徑有關的內建模組

  • path.basename()取得一個路徑的最後一部分檔名
  • path.normalize()幫助修正路徑
  • path.join()用於路徑拼接(引數為多個路徑引數)
  • path.resolve()將一個相對路徑解析為絕對路徑
  • {basename, dirname, extname}
    • basename 完整名
    • dirname 上級路徑名
    • extname 字尾名
  • {parse, format}
    • parse用於解析當前路徑為一個json格式的資料
    • format相當於格式化json資料為一個字串

說明:__dirname__filename總是返回檔案的絕對路徑

process.cwd()總是返回node命令所在資料夾

3.2 Buffer

三個要點:

  • Buffer用於處理二進位制資料流
  • 例項類似整數陣列,大小固定
  • C++程式碼在V8堆外分配實體記憶體

Buffer常用的方法

  • byteLength統計buffer所佔位元組數
  • isBuffer用來判斷目標資料是不是一個Buffer
  • concat合併連線Buffer
  • from將目標資料轉換為Buffer
  • toString用來轉換Buffer為字串

3.3 events

  • eventEmitter.on('eventName',callback())用於註冊監聽器
  • eventEmitter.emit('eventName')用於觸發事件
const EventEmitter = require('events');

class CustomEvent extends EventEmitter {
    
}

const ce = new CustomEvent();

ce.on('eventName',callback);

ce.emit('eventName','your msg to eventEmit',....);

//有一個通用引數就叫error

ce.on('error',fn);

//Example
ce.on('error',(err, item) => {
    console.log(err);
    console.log(item);
});

ce.emit('error', new Error('出錯了'), Date().now);
複製程式碼

針對事件只需要響應一次:

ce.once('test', ()=> {
    console.log(test);
});


複製程式碼

針對事件需要移除的話:

ce.removeListener('eventName',fn);

//or

ce.removeAllListeners('test');
複製程式碼

3.4 fs

​ 首先需要注意的就是Node.js的設計模型就是錯誤優先的模式。

fs.readFile('fileUrl', 'utf8', (err, data) => {
    if(err) throw err;
    
    console.log(data);
})
複製程式碼
  • stat()檢視檔案的詳細資訊

    const fs = require('fs');
    
    fs.stat('fileUrl', (err, data) => {
        if (err) {
            throw err;//這裡可以判斷檔案是否存在
        }
        
        console.log(data);
    });
    複製程式碼
  • rename()更改檔名

    fs.rename('./text.txt','hahah.ttx');
    複製程式碼
  • unlink刪除檔案

fs.unlink('fileName', err => err);
複製程式碼
  • readdir()讀取資料夾
  • mkdir()建立資料夾
  • rmdir()刪除資料夾
  • watch()監視檔案或目錄的變化
fs.watch('fileUrl', {
    recursive:true //是否監視子資料夾
}, (eventType, fileName) => {
    console.log(eventType, fileName);
})
複製程式碼
  • readStream()讀取流
const rs = fs.createReadStream('urlPath');

rs.pipe(process.stdout);//匯出檔案到控制檯
複製程式碼
  • writeStream()寫入流

  • pipe()管道,導通流檔案

    const ws  = fscreateWriteStream('urlPath');
    
    ws.write('some content');
    
    ws.end();
    
    ws.on('finish',()=>{
       console.log('done!!!'); 
    });
    複製程式碼

第四篇 專案Init

4.1 .gitignore

  • 匹配模式前/代表專案根目錄
  • 匹配模式最後加\代表是目錄
  • 匹配模式前加!代表取反
  • *代表任意一個字元
  • ?匹配任意字元
  • **匹配任意目錄

4.2 ESLint

完整配置.eslintrc.js

module.exports = {
    env: {
        es6: true,
        mocha: true,
        node: true
    },
    extends: ['eslint:recommended', 'plugin:sort-class-members/recommended'],
    parser: 'babel-eslint',
    plugins: ['babel', 'sort-class-members'],
    root: true,
    rules: {
        'accessor-pairs': 'error', // 強制 getter 和 setter 在物件中成對出現
        'array-bracket-spacing': 'off', // 強制陣列方括號中使用一致的空格
        'arrow-parens': 'off', // 要求箭頭函式的引數使用圓括號
        'arrow-spacing': 'error', // 強制箭頭函式的箭頭前後使用一致的空格
        'babel/arrow-parens': ['error', 'as-needed'],
        'babel/generator-star-spacing': ['error', 'before'],
        'block-scoped-var': 'error', // 強制把變數的使用限制在其定義的作用域範圍內
        'block-spacing': 'off', // 強制在單行程式碼塊中使用一致的空格
        'brace-style': 'off', // 強制在程式碼塊中使用一致的大括號風格
        camelcase: 'off', // 強制使用駱駝拼寫法命名約定
        'comma-dangle': 'off', // 要求或禁止末尾逗號
        'comma-spacing': 'off', // 強制在逗號前後使用一致的空格
        'comma-style': 'off', // 強制使用一致的逗號風格
        complexity: 'off', // 指定程式中允許的最大環路複雜度
        'computed-property-spacing': 'error', // 強制在計算的屬性的方括號中使用一致的空格
        'consistent-return': 'off', // 要求 return 語句要麼總是指定返回的值,要麼不指定
        'consistent-this': 'error', // 當獲取當前執行環境的上下文時,強制使用一致的命名
        'constructor-super': 'error', // 要求在建構函式中有 super() 的呼叫
        curly: 'off', // 強制所有控制語句使用一致的括號風格
        'default-case': 'error', // 要求 switch 語句中有 default 分支
        'dot-location': ['error', 'property'], // 強制在點號之前和之後一致的換行
        'dot-notation': 'off', // 強制在任何允許的時候使用點號
        'eol-last': 'off', // 強制檔案末尾至少保留一行空行
        eqeqeq: ['error', 'smart'], // 要求使用 === 和 !==
        'func-names': 'off', // 強制使用命名的 function 表示式
        'func-style': ['error', 'declaration', { // 強制一致地使用函式宣告或函式表示式
            allowArrowFunctions: true
        }],
        'generator-star-spacing': 'off', // 強制 generator 函式中 * 號周圍使用一致的空格
        'id-length': ['error', { // 強制識別符號的最新和最大長度
            exceptions: ['_', 'e', 'i', '$']
        }],
        indent: ['error', 4, { // 強制使用一致的縮排
            SwitchCase: 1
        }],
        'key-spacing': 'off', // 強制在物件字面量的屬性中鍵和值之間使用一致的間距
        'keyword-spacing': ['off', { // 強制在關鍵字前後使用一致的空格
            overrides: {
                case: {
                    after: true
                },
                return: {
                    after: true
                },
                throw: {
                    after: true
                }
            }
        }],
        'linebreak-style': 'off',
        'lines-around-comment': 'off',
        'max-depth': 'error', // 強制可巢狀的塊的最大深度
        'max-nested-callbacks': 'off',
        'max-params': ['error', 4],
        'new-cap': 'off',
        'new-parens': 'error', // 要求呼叫無參建構函式時有圓括號
        'newline-after-var': 'off',
        'no-alert': 'error', // 禁用 alert、confirm 和 prompt
        'no-array-constructor': 'error', // 禁止使用 Array 建構函式
        'no-bitwise': 'error', // 禁用按位運算子
        'no-caller': 'error', // 禁用 arguments.caller 或 arguments.callee
        'no-catch-shadow': 'off',
        'no-class-assign': 'error', // 禁止修改類宣告的變數
        'no-cond-assign': ['error', 'always'], // 禁止條件表示式中出現賦值操作符
        'no-confusing-arrow': 'error', // 不允許箭頭功能,在那裡他們可以混淆的比較
        "no-console": 0,
        'no-const-assign': 'error', // 禁止修改 const 宣告的變數
        'no-constant-condition': 'error', // 禁止在條件中使用常量表示式
        'no-div-regex': 'error', // 禁止除法操作符顯式的出現在正規表示式開始的位置
        'no-dupe-class-members': 'error', // 禁止類成員中出現重複的名稱
        'no-duplicate-imports': 'off', // disallow duplicate module imports
        'no-else-return': 'error', // 禁止 if 語句中有 return 之後有 else
        'no-empty-label': 'off',
        'no-empty': 'off',
        'no-eq-null': 'error', // 禁止在沒有型別檢查操作符的情況下與 null 進行比較
        'no-eval': 'error', // 禁用 eval()
        'no-extend-native': 'error', // 禁止擴充套件原生型別
        'no-extra-bind': 'error', // 禁止不必要的 .bind() 呼叫
        'no-extra-parens': 'error', // 禁止不必要的括號
        'no-floating-decimal': 'error', // 禁止數字字面量中使用前導和末尾小數點
        'no-implied-eval': 'error', // 禁止使用類似 eval() 的方法
        'no-inline-comments': 'error', // 禁止在程式碼行後使用內聯註釋
        'no-iterator': 'error', // 禁用 __iterator__ 屬性
        'no-label-var': 'off',
        'no-labels': 'off',
        'no-lone-blocks': 'error', // 禁用不必要的巢狀塊
        'no-lonely-if': 'off',
        'no-loop-func': 'error', // 禁止在迴圈中出現 function 宣告和表示式
        'no-mixed-requires': 'error', // 禁止混合常規 var 宣告和 require 呼叫
        'no-mixed-spaces-and-tabs': 'off',
        'no-multi-spaces': 'off',
        'no-multi-str': 'off',
        'no-native-reassign': 'error', // 禁止對原生物件賦值
        'no-nested-ternary': 'error', // 不允許使用巢狀的三元表示式
        'no-new-func': 'error', // 禁止對 Function 物件使用 new 操作符
        'no-new-object': 'error', // 禁止使用 Object 的建構函式
        'no-new-require': 'error', // 禁止呼叫 require 時使用 new 操作符
        'no-new-wrappers': 'error', // 禁止對 String,Number 和 Boolean 使用 new 操作符
        'no-new': 'error', // 禁止在非賦值或條件語句中使用 new 操作符
        'no-octal-escape': 'error', // 禁止在字串中使用八進位制轉義序列
        'no-path-concat': 'error', // 禁止對 __dirname 和 __filename進行字串連線
        'no-process-env': 'error', // 禁用 process.env
        'no-process-exit': 'error', // 禁用 process.exit()
        'no-proto': 'error', // 禁用 __proto__ 屬性
        'no-restricted-modules': 'error', // 禁用指定的通過 require 載入的模組
        'no-return-assign': 'error', // 禁止在 return 語句中使用賦值語句
        'no-script-url': 'error', // 禁止使用 javascript: url
        'no-self-compare': 'error', // 禁止自身比較
        'no-sequences': 'error', // 禁用逗號操作符
        'no-shadow-restricted-names': 'error', // 禁止覆蓋受限制的識別符號
        'no-shadow': 'off',
        'no-spaced-func': 'off',
        'no-sync': 'off', // 禁用同步方法
        'no-this-before-super': 'error', // 禁止在建構函式中,在呼叫 super() 之前使用 this 或 super
        'no-throw-literal': 'error', // 禁止丟擲非異常字面量
        'no-trailing-spaces': 'error', // 禁用行尾空格
        'no-undef-init': 'error', // 禁止將變數初始化為 undefined
        'no-undefined': 'off',
        'no-underscore-dangle': 'off',
        'no-unexpected-multiline': 'error', // 禁止出現令人困惑的多行表示式
        'no-unneeded-ternary': 'error', // 禁止可以在有更簡單的可替代的表示式時使用三元操作符
        'no-unused-expressions': 'error', // 禁止出現未使用過的表示式
        "no-unused-vars": [1, { // 禁止出現未使用過的變數
            "vars": "all",
            "args": "after-used"
        }],
        'no-use-before-define': 'error', // 不允許在變數定義之前使用它們
        'no-useless-call': 'error', // 禁止不必要的 .call() 和 .apply()
        'no-useless-concat': 'error', // 禁止不必要的字串字面量或模板字面量的連線
        'no-var': 'off',
        'no-void': 'error', // 禁用 void 操作符
        'no-warning-comments': 'off',
        'no-with': 'off',
        'object-curly-spacing': 'off',
        'object-shorthand': 'error', // 要求或禁止物件字面量中方法和屬性使用簡寫語法
        'one-var': 'off',
        'operator-assignment': 'error', // 要求或禁止在可能的情況下要求使用簡化的賦值操作符
        'operator-linebreak': 'off',
        'padded-blocks': 'off',
        'prefer-arrow-callback': 'off',
        'prefer-const': 'error', // 要求使用 const 宣告那些宣告後不再被修改的變數
        'prefer-spread': 'error', // 要求使用擴充套件運算子而非 .apply()
        'prefer-template': 'off', // 要求使用模板字面量而非字串連線
        quotes: 'off',
        'quote-props': 'off',
        radix: 'error', // 強制在parseInt()使用基數引數
        'require-yield': 'error', // 要求generator 函式內有 yield
        "semi": ["error", "always"], // 要求或禁止使用分號
        'semi-spacing': 'off',
        'sort-vars': 'error', // 要求同一個宣告塊中的變數按順序排列
        'space-before-blocks': 'off',
        'space-before-function-paren': 'off',
        'space-in-parens': 'off',
        'space-infix-ops': 'off',
        'space-unary-ops': 'off',
        'spaced-comment': 'off',
        strict: 'off',
        'valid-jsdoc': 'error', // 強制使用有效的 JSDoc 註釋
        'vars-on-top': 'off',
        yoda: 'off',
        'wrap-iife': 'off',
        'wrap-regex': 'error' // 要求正規表示式被括號括起來
    }
}
複製程式碼

第五篇 靜態資源伺服器

5.1 http模組

const http = require('http');
const chalk = require('chalk');
const hostname = '127.0.0.1';

const port = '3000';
const server = http.createServe((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World');
});

server.listen(port, hostname, () => {
    const addr = `Server running at http://${hostname}:${port}/`;
    console.log(`${chalk.green(addr)}`);
})
複製程式碼
  • supervisor監視檔案模組
  • hotnode熱啟動模組

完整程式碼:請參照

第六篇 測試

6.1 斷言測試

  • assert- 斷言除錯
console.assert(a===b,msg);
複製程式碼

常用斷言庫:chai.js

6.2 mocha.js

//you should install mocha

// $ npm install --save-dev mocha

// you should know the '#math' only is it's description
describe('#math', ()=> {
    describe('#add', ()=> {
        it('should return 5 when 2 + 3', () => {
            ecpect('your test','targetResult');
        });
    });
});
複製程式碼

Note :如果你想只執行一個模組的,你可以在package.json中配置

"scripts": {
  "test": "mocha /url/xxx.js
}
複製程式碼

6.3 istanbul

你可以參照

6.4 benchmark.js

  • 效能測試框架,具體參見
//常見測試方法
console.time('markPoint');

console.timeEnd('markPoint');
複製程式碼

6.5 jsbench.js

  • 在FCC社群聽老外告訴的,自己做了測試,確實是可以測試兩段程式碼的效能差異。

第七篇 UI測試

7.1 傳統測試

## 傳統測試自然就是人工點選

## 弊端就是發現錯誤不健全
複製程式碼

7.2 webdriver

參照

第八篇 爬蟲

8.1 爬蟲與反爬蟲

==爬蟲==:按照一定的規則自動抓取網路資訊的程式

==反爬蟲==:

  • User-Agent,Referer,驗證碼
  • 單位時間的訪問次數,訪問量
  • 關鍵資訊用圖片混淆
  • 非同步載入

8.2 爬蟲思路

思路一:使用cheerio與http/https

  • cheerio:一個可以像jQuery那樣操作DOM元素
  • http/https:我就不用說了吧
//簡單示例
const cheerio = require('cheerio');
const https = require('https');
let html = '';
const $ = '';
https.get('url',res => {
    res.on('data',(data) => {
        html += data;
    });
    res.on('finish',()=> {
        $ = cheerio.load(html);
        //some code
    })
})
複製程式碼

思路二:puppeteer

  • 下邊貼一段爬蟲程式碼吧!

index.js

const puppeteer = require('puppeteer');
const https = require('https');
const fs = require('fs');
(async () => {
    //跳過安裝  npm i --save puppeteer --ignore-scripts
    const browser = await puppeteer.launch({
        executablePath: 'G:/chrome-win/chrome-win/chrome.exe'
    });
    const page = await browser.newPage();
    //指定瀏覽器去某個頁面
    await page.goto('https://image.baidu.com');
    // await page.cookies('https://image.baidu.com')
    //     .then(data => {
    //         console.info(data)
    //     });
    //調大視口,方便截圖,方便容納更多地圖
    await page.setViewport({
        width:2000,
        height:1000,
    });
    //模擬用哪個戶輸入
    await page.keyboard.sendCharacter('車模');
    //模擬使用者點選搜尋
    await page.click('.s_search');
    console.info('開始點選查詢......');
    //await page.screenshot({path: 'example.png'});
     page.on('load', async()=> {
         console.info('頁面已載入完畢,開始爬取');
        await page.screenshot({path: 'example.png'});
          let srcs = await page.evaluate(() => {
             let img =  document.querySelectorAll('img.main_img');
             return Array.from(img).map(item => item.src);
         });
         await srcs.forEach((item,index) => {
                  if(/^https:/g.test(item))
                  https.get(item,res =>{
                      res.pipe(fs.createWriteStream(__dirname + `/img/${index}.png`))
                          .on('finish',()=>{
                              console.info(`已成功爬取${index + 1}條記錄!`);
                          });
                  })
              })
         await browser.close();
    });
     
})();
複製程式碼

package.json

{
  "name": "reptiles",
  "version": "1.0.0",
  "description": "This is a replite",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "vuejs@vip.qq.com",
  "license": "ISC",
  "dependencies": {
    "puppeteer": "^1.14.0"
  }
}

複製程式碼
  • How To Use?
## 初始化你的專案

$ npm init

## 建立檔案

$ cd your project
$ mkdir index.js
## 拷貝我的package.json
## 注意,這是重點,因為被牆,所以必須跳過安裝
$ npm i --save puppeteer --ignore-scripts

## 啟動專案

$ npm start

複製程式碼
  • Note SomeDetails
    • 1.安裝puppeteer,必須跳過Chromium的連線
    • 2.安裝好你的Chromium,在lanuch的時候必須用絕對路徑,切記不可把chrom.exe單純拷貝到專案目錄
    • 3.不可把瀏覽器視口調節的太大,不然會觸碰反爬蟲協議。
    • 4.如果你調大了也沒關係,我們可以page.waitFor(200),讓任務沉睡一會兒

結語

  這篇大長文就算徹底結束了,我相信文章裡面還有很多不足或者小錯誤,誠懇的希望大家不要噴我,我現在僅僅是個大三的小學生,在以後的工作中,我也會不斷的吸取知識的精髓去修繕這篇文章。

​    我熱愛學習,當然更加熱愛程式碼。有一句至理名言分享給大家

​    "你能不能,在於你想不想!"

​    加油,每一天都是新生。

​    對於這篇文章,我也會不斷的完善它。


​   最後再做一遍自我介紹:

​   我叫EricWang,今年大三,是個藍孩紙,想跟我一起交流就關注我吧!

免責宣告

​   本文禁止貼上複製,畢竟算是尊重我的一點點小勞動力 ,當然貼上複製,我也沒辦法,你行行好,註明出處。Emmm,就這麼多吧!祝願大家學習進步,工作順利!

相關文章