zoukankan      html  css  js  c++  java
  • Node.js学习笔记

    1.1. Part 0 :Node.js简介

    1.1.1. a)Node.js简介

    Node.js不是一门语言也不是框架,他只是基于Google V8引擎的JavaScript运行时环境,同时结合Libuv扩展了JavaScript功能,使之支持io、fs等只有语言才有的特性,使得JavaScript能够同时具有DOM操作(浏览器)I/O文件读写操作数据库(服务器端)等能力,是目前最简单的全栈式语言

    Node.js通常被用来开发低延迟的网络应用,也就是那些需要在服务器端环境和前端实时收集交换数据的应用(API、即时聊天、微服务)。

    1.1.2. b)什么是Node.js?

    • Node.js不是JavaScript应用,不是语言(JavaScript是语言),不是想Rails(Ruby)或Django(Python)一样的框架,也不是想Nginx一样的Web服务器。Node.js是JavaScript运行时环境
    • 构建在Chrome's V8这个著名的JavaScript引擎之上,Chrome V8引擎以C/C++ 为主,相当于使用JavaScript写法,转成C/C++ 调用,大大的降低了学习成本
    • 事件驱动(event-driven),非阻塞 I/O 模型,简单点讲就是每个函数都是异步的,最后由Libuv这个C/C++编写的事件循环处理库来处理这些 I/O操作,隐藏了非阻塞 I/O 的具体细节,简化并发变成模型,让你可以轻松的编写共性能的Web应用,所以他是轻量且高效的
    • 使用 npm 作为包管理器,目前 npm 是开源库里包管理最大的生态,功能强大,截止到2017年12月,模块数量超过60W+

    总结:Node.js是构建在JavaScript之上的,事件触发和异步的,专为数据密集型实时程序设计的。其目标是让并发编程更简单,,主要应用在以网络编程为主的 I/O 密集型应用。他是开源的,跨平台,并且高效(尤其是 I/O 处理)

    1.1.3. c)基本原理

    下面是一张 Node.js 早期的架构图,来自 Node.js 之父 Ryan Dahl 的演讲稿,在今天依然不过时,它简要的介绍了 Node.js 是基于 Chrome V8引擎构建的,由事件循环(Event Loop)分发 I/O 任务,最终工作线程(Work Thread)将任务丢到线程池(Thread Pool)里去执行,而事件循环只要等待执行结果就可以了。

    核心概念

    • Chrome V8是 Google 发布的开源JavaScript 引擎,采用C/C++ 编写,在Google的 Chrome浏览器中被使用。Chrome V8 引擎可以独立运行,也可以用来嵌入到C/C++ 应用程序中执行。
    • Event Loop事件循环(由Libuv提供)
    • Thread Pool线程池(由Libuv提供)

    梳理一下

    • Chrome V8 是 JavaScript 引擎
    • Node.js 内置 Chrome V8 引擎,所以它使用的 JavaScript 语法
    • JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
    • 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
    • 如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 I/O 很慢,不得不等着结果出来,再往下执行
    • CPU 完全可以不管 I/O 设备,挂起处于等待中的任务,先运行排在后面的任务
    • 将等待中的 I/O 任务放到 Event Loop 里
    • 由 Event Loop 将 I/O 任务放到线程池里
    • 只要有资源,就尽力执行

     

    核心

    • Chrome V8 解释并执行 JavaScript 代码(这就是为什么浏览器能执行 JavaScript 原因)
    • libuv 由事件循环和线程池组成,负责所有 I/O 任务的分发与执行

    在解决并发问题上,异步是最好的解决方案,可以拿排队和叫号机来理解

    • 排队:在排队的时候,你除了等之外什么都干不了
    • 叫号机:你要做的是先取号码,等轮到你的时候,系统会通知你,这中间,你可以做任何你想做的事儿

    1.3.5. Node.js应用场景

    Node.js简称 数据密集型实时程序(DIRT)。因为Node.js自身在I/O 上非常轻量,它善于将数据从一个管道混排或代理到另一个官道上,这能在处理大量请求时持有很多开放的连接,并且只占用一小部分内存。它的设计目标是保证影响能力,跟浏览器一样。

    Node.js使用场景主要分为4大类:

    • 1)跨平台:覆盖你能想到的面向用户的所有平台,传统的PC Web端,以及PC客户端 nw.js/electron 、移动端 cordova、HTML5、react-nativeweex,硬件 ruff.io 等
    • 2)Web应用开发:网站、Api、RPC服务等
    • 3)前端:三大框架 React  Vue  Angular 辅助开发,以及工程化演进过程(使用Gulp/Webpack 构建 Web 开发工具)
    • 4)工具:npm上各种工具模块,包括各种前端预编译、构建工具 Grunt / Gulp、脚手架,命令行工具,各种奇技淫巧等

    1.3.6. Node核心:异步流程控制

     Node.js是为异步而生的,他把复杂的事情都给做了(高并发,低延时),交给用户的只是有点难用的Callback写法。

    直面问题才能有更好的解决方式,Node.js的异步是整个学习Node.js过程中的重中之重。

    • 1)异步流程控制学习重点
    • 2)Api 写法:Error-first Callback和EventEmitter
    • 3)中流砥柱:Promise
    • 4)终极解决方案:Async/Await

    1.3.6.2. 2)Api写法:Error-first Callback 和 EventEmitter

    a)Error-first Callback 定义错误优先的回调写法只需要注意2条规则即可:

    • 回调函数的第一个参数返回的error对象,如果error发生了,它会作为第一个err参数返回,如果没有,一般做法是返回null。
    • 回调函数的第二个参数返回的是任何成功响应的结果数据。如果结果正常,没有error发生,err会被设置为null,并在第二个参数就出返回成功结果数据。

    b)EventEmitter

    事件模块是 Node.js 内置的对观察者模式“发布/订阅”(publish/subscribe)的实现,通过EventEmitter属性,提供了一个构造函数。该构造函数的实例具有 on 方法,可以用来监听指定事件,并触发回调函数。任意对象都可以发布指定事件,被 EventEmitter 实例的 on 方法监听到。

     Node.js的API都是异步的,同步的函数是奢求,要查API文档,在高并发场景下慎用。

    1.3.6.3. 3)中流砥柱:Promise

    回调地狱

    Node.js 因为采用了错误优先的回调风格写法,导致sdk里导出都是回调函数。如果组合调用的话,就会特别痛苦,经常会出现回调里嵌套回调的问题,大家都非常厌烦这种写法,称之为Callback Hell,即回调地狱。一个经典的例子来自著名的Promise模块q文档里。

    step1(function (value1) {
        step2(value1, function(value2) {
            step3(value2, function(value3) {
                step4(value3, function(value4) {
                    // Do something with value4
                });
            });
        });
    });

    Promise意味着[许愿|承诺]一个还没有完成的操作,但在未来会完成的。与Promise最主要的交互方法是通过将函数传入它的then方法从而获取得Promise最终的值或Promise最终最拒绝(reject)的原因。要点有三个:

    • 递归,每个异步操作返回的都是promise对象
    • 状态机:三种状态转换,只在promise对象内部可以控制,外部不能改变状态
    • 全局异常处理

     1)定义

    var promise = new Promise(function(resolve, reject) {
      // do a thing, possibly async, then…
    
      if (/* everything turned out fine */) {
        resolve("Stuff worked!");
      }
      else {
        reject(Error("It broke"));
      }
    });

    每个Promise定义都是一样的,在构造函数里传入一个匿名函数,参数是 resolve 和 reject,分别代表成功和失败时候的处理。

    2)调用

    promise.then(function(text){
        console.log(text)// Stuff worked!
        return Promise.reject(new Error('我是故意的'))
    }).catch(function(err){
        console.log(err)
    })

    它的主要交互方式是通过then函数,如果Promise成功执行resolve了,那么它就会将resolve的值传给最近的then函数,作为它的then函数的参数。如果出错reject,那就交给catch来捕获异常就好了。

    Promise 的最大优势是标准化,各类异步工具库都按照统一规范实现,即使是async幻术也可以无缝集成。所以用 Promise 封装API通用性强,用起来简单,学习成本低。在async幻术普及之前,绝大部分应用都是采用 Promise来做异步流程控制的,所以掌握 Promise是Node.js学习必须要会的。

    1.3.6.4. 4)终极解决方案:Async/Await

    Async/Await是异步操作的终极解决方案,Koa 2在node 7.6发布之后,立马发布了正式版本,并且推荐使用async函数来编写Koa中间件。

    这里给出一段Koa 2应用里的一段代码

    exports.list = async (ctx, next) => {
      try {
        let students = await Student.getAllAsync();
    
        await ctx.render('students/index', {
          students : students
        })
      } catch (err) {
        return ctx.api_error(err);
      }
    };

    它做了3件事儿

    • 通过await Student.getAllAsync();来获取所有的students信息。
    • 通过await ctx.render渲染页面
    • 由于是同步代码,使用try/catch做的异常处理

    4.1 正常写法

    const pkgConf = require('pkg-conf');
    
    async function main(){
        const config = await pkgConf('unicorn');
    
        console.log(config.rainbow);
        //=> true
    }
    main();

    变态写法

    const pkgConf = require('pkg-conf');
    
    (async () => {
        const config = await pkgConf('unicorn');
    
        console.log(config.rainbow);
        //=> true
    })();

    4.2 await + Promise

    const Promise = require('bluebird');
    const fs = Promise.promisifyAll(require("fs"));
    
    async function main(){
        const contents = await fs.readFileAsync("myfile.js", "utf8")
        console.log(contents);
    }
    main();

    4.3 await + co + generator

    const co = require('co');
    const Promise = require('bluebird');
    const fs = Promise.promisifyAll(require("fs"));
    
    async function main(){
       const contents = co(function* () {
          var result = yield fs.readFileAsync("myfile.js", "utf8")
          return result;
       })
        console.log(contents);
    }
    main();

    由上面3中基本用法可以推出Async函数要点如下:

    • Async函数语义上非常好
    • Async不需要执行器,它本身具备执行能力,不像Generator需要co模块
    • Async函数的异常处理采用try/catch和Promise的错误处理,非常强大
    • Await接Promise,Promise自身就足够应对所有流程了,包括async函数没有纯并行处理机制,也可以采用Promise里的all和race来补齐
    • Await释放Promise的组合能力,外加co和Promise的then,几乎没有不支持的场景

    小结

    这部分共讲了4个小点,都是极其直接的必须掌握的知识点。

    • 1) 异步流程控制学习重点
    • 2)Api写法:Error-first Callback 和 EventEmitter
    • 3)中流砥柱:Promise
    • 4)终极解决方案:Async/Await


     这里有一个练手的项目,感兴趣的同学可以自行Download,相信会有所收获https://github.com/oceanMin/cms

  • 相关阅读:
    ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
    ios开发Base64编码以及加密相关学习
    ios开发网络学习AFN三:AFN的序列化
    iOS开发AFN使用二:AFN文件下载与文件上传
    Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.4——更新新版本的Gradle
    Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.3——用Eclipse ADT导出App
    Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.2——将Eclipse开发的项目导入到AndroidStudio
    Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.1——设置项目参数
    Android开发:《Gradle Recipes for Android》阅读笔记1.7——仓库配置
    Android开发:《Gradle Recipes for Android》阅读笔记1.6——使用android studio添加依赖
  • 原文地址:https://www.cnblogs.com/minjh/p/9531225.html
Copyright © 2011-2022 走看看