Node.js的异步是整个学习Node.js过程中重中之重。

1)异步流程控制学习重点

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

3)中流砥柱:Promise

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

node.js必知必会

  1. .Node.js SDK里callback写法必须会的。

  2. Node.js学习重点: Async函数与Promise

    1.中流砥柱:Promise

    2.终极解决方案:Async/Await

1. Api 写法:Error-first Callback 和EventEmitter

1.1 Error-first Callback

定义错误有限的回调写法只需要注意2条规则即可:

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

下面让我们看一下调用函数示例,Node.js文档例最常采用下面这样的回调方式:

function(err,res){
//process the error and result
}

这里的 callback 指的是带有2个参数的函数:“err”和“res” 。语义上讲,非空的"err"相当于程序异常;而空的“err”相当于可以正常返回结果“res”,无任何异常。

1.2 EventEmitter

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

在node6之后,可以直接使用require('events')类

var EventEmitter = require('events')
var util = require('util')
var MyEmitter = function(){ }
util.inherits(MyEmitter,EventEmitter) const myEmitter = new MyEmitter(); myEmitter.on('event',(a,b)=>{
console.log(a,b,this)
// Prints: a b {}
})
myEmitter.emit('event','a','b')

和jquery、vue 里的Event 是非常类似的。而且前端自己也有EventEmitter。

1.3 如何更好的查Node.js文档

API 是应用程序接口 Application Programming interface的简称。从Node.js异步原理,我们可以知道,核心在于Node.js SDK中API调用,然后交由EventLoop(Libuv)去执行,所以我们一定要熟悉Node.js的API操作。

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

笔者推荐使用 DeshZeal查看离线文档,对api理解会深入很多,比IDE辅助要好,可以有效避免离开IDE就不会写代码的窘境。

1.4 中流砥柱:Promise

回调地狱

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

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

这里只是做4步,嵌套了4层回调,如果更多步骤呢?很多新手浅尝辄止,到这儿就望而却步,粉转黑。这明显不够成熟,起码你要看看它的对应解决方案吧!

Promise最早也是在commonjs社区提出来的,当时提出了很多规范。比较接受的是promise/A规范。后来人们在这个基础上,提出了promise/A+规范,也就是实际上现在的业内推行的规范。ES6 也是采用的这种规范。

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

  1. 递归,每个异步操作返回的都是promise对象
  2. 状态机: 三种状态转换,只在promise对象内部可以控制,外部不能改变状态
  3. 全局异常处理
定义
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,分别代表成功和失败时候的处理。

调用
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学习过程中必须要掌握的重中之重。

Bluebird是 Node.js 世界里性能最好的Promise/a+规范的实现模块,Api非常齐全,功能强大,是原生Promise外的不二选择。

好处如下:

  1. 避免Node.js内置Promise实现 问题,使用与所有版本兼容
  2. 避免Node.js 4曾经出现的内存泄露问题
  3. 内置更多扩展,timeout、 promisifyAll等,对Promise/A+规范提供了强有力的补充

在学习Node.js过程中,对于Promise了解多深入都不过分。

推荐学习资料

  1. Node.js最新技术栈之Promise篇 https://cnodejs.org/topic/560dbc826a1ed28204a1e7de
  2. 理解 Promise 的工作原理 https://cnodejs.org/topic/569c8226adf526da2aeb23fd
  3. Promise 迷你书 http://liubin.github.io/promises-book/

1.4 终极解决方案:Async/Await

Async/Await是异步操作的终

极解决方案,Koa 2在node 7.6发布之后,立马发布了正式版本,并且推荐使用async函数来编写Koa中间件。

这里给出一段Koa2应用里的一段代码:

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件事儿

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

是不是非常简单,现在Eggjs里也都是这样同步的代码。

(1)正常写法
const pkgConf = require('pkg-conf')
async function main(){
const config = await pkgConf('unicorn');
console.log(config.ranbow);
//true
}
main()

变态写法:

const pkgConf = require('pkg-conf');
(async ()=> {
const config = await pkgConf('unicorn') console.log(config.ranbow)
//true
})()
(2)await + Promise
const Promise = require('bluedbird');
const fs = Promise.promisifyAll(require("fs")); async function main(){
const contents = await fs.readFileAsync("myfile.js","utf8")
console.log(contents)
} main()
(2)await + co + generator
const co = require('co')
const Promise = require('bluedbird')
const fs = Promise.promisifyAll(require("fs")) async function mian(){
const contents = co(function* () {
var result = yield fs.readFileAsync("myfile.js","utf8")
return result;
})
console.log(contents) }
main()

要点:

  • co 的返回值是promise,所以await 可以直接接co.
  • co 的参数是generator
  • 在gennerator 里可以使用yield,而yield后面接的有5种可能,故而把这些可以yeild接的方式称为yieldable,即可以yield接的。
    • Promises
    • Thunks(functions)
    • array(parallel execution)
    • objects (parallel execution)
    • Generators 和 GeneratorFunctions

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

  • Async函数语义上非常好
  • Async不需要执行器,它本身具备执行能力,不像Generator需要co模块
  • Async函数的异常处理采用try/catch和Promise的错误处理,非常强大
  • co作为Generator执行器是不错的,它更好的是当做Promise 包装器,通过Generator支持yieldable,最后返回Promise,是不是有点无耻?
小结:

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

  • 异步流程控制学习重点

    • 2)Api写法:Error-first Callback 和 EventEmitter
    • 3)中流砥柱:Promise
    • 4)终极解决方案:Async/Await

这里再提一下关于Node.js源码阅读问题,很多人api都还没完熟练就去阅读源码,这是非常不赞成的,不带着问题去读源码是比较容易迷失在大量代码中的。效果并不好。

先用明白,然后再去阅读Node.js源码,然后探寻libuv并发机制。很多人买了朴大的《深入浅出Node.js》一书,看了之后还是不太会用,不是书写的不好,而是步骤不对。

  1. Node in action和了不起的Node.js是入门的绝好书籍,非常简单,各个部分都讲了,但不深入,看了之后,基本就能用起来了
  2. 当你用了一段之后,你会对Node.js的运行机制好奇,为啥呢?这时候去读朴大的《深入浅出Node.js》一书就能够解惑。原因很简单,九浅一深一书是偏向底层实现原理的书,从操作系统,并发原理,node源码层层解读。如果是新手读,难免会比较郁闷。
  3. 实践类的可以看看雷宗民(老雷)和赵坤(nswbmw)写的书

我一般给大家的推荐是把Node in action读上5遍10遍,入门干活足够了。剩下的就是反复实践,多写代码和npm模块就好。

迷茫时学习Node.js最好的方法

Node.js 编写的包管理器 npm 已成为开源包管理了领域最好的生态,直接到2017年10月份,有模块超过47万,每周下载量超过32亿次,每个月有超过700万开发者使用npm。现在早已经超过60万个模块了。

这里就不一一举例了,给出一个迷茫时学习Node.js最好的方法吧!

"每天看10个npm模块"

对于学习Node.js迷茫的人来说,这是最好的方式,当你不知道如何做的时候,就要向前(钱)看,你要知道积累哪些技能对以后有好处。对于学习Node.js必经之路,一定是要掌握很多模块用法,并从中汲取技巧、思路、设计思想的。与其不知道学什么,为什么不每天积累几个技巧呢?

推荐一个repo即 https://github.com/parro-it/awesome-micro-npm-packages 小型库集合,一天看十个不是梦!

更多讨论 https://zhuanlan.zhihu.com/p/29625882

此为学习node的学习笔记,为了自己学习查阅,转自狼叔的教程,如果侵权请联系我删除

阅读原文

node核心:异步流程控制的更多相关文章

  1. js 异步流程控制之 avQ(avril.queue)

    废话前言 写了多年的js,遇到过最蛋疼的事情莫过于callback hell, 相信大家也感同身受. 业界许多大大也为此提出了很多不错的解决方案,我所了解的主要有: 朴灵 event proxy, 简 ...

  2. 使用yield进行异步流程控制

    现状 目前我们对异步回调的解决方案有这么几种:回调,deferred/promise和事件触发.回调的方式自不必说,需要硬编码调用,而且有可能会出现复杂的嵌套关系,造成"回调黑洞" ...

  3. Nodejs中使用异步流程控制Async

    首先,我们都知道,Node基于事件驱动的异步I/O架构,所谓异步就是非阻塞,说白了就是一个事件执行了,我不必等待它执行完成后我才能执行下一个事件.所以在Node环境中的模块基本都是异步的,上一篇说到我 ...

  4. nodejs进阶(7)—async异步流程控制

    Async介绍 Async是一个流程控制工具包,提供了直接而强大的异步功能.基于Javascript为Node.js设计,同时也可以直接在浏览器中使用. Async提供了大约20个函数,包括常用的 m ...

  5. 异步流程控制库GoWithTheFlow

    异步流程控制库GoWithTheFlow 一个尾触发方式来控制异步流程的库, 有seq(顺序执行) par(同步执行) 两种方法 博客 http://notes.jetienne.com/2011/0 ...

  6. 【javascript】Promise/A+ 规范简单实现 异步流程控制思想

    ——基于es6:Promise/A+ 规范简单实现 异步流程控制思想  前言: nodejs强大的异步处理能力使得它在服务器端大放异彩,基于它的应用不断的增加,但是异步随之带来的嵌套.难以理解的代码让 ...

  7. node基础13:异步流程控制

    1.流程控制 因为在node中大部分的api都是异步的,比如说读取文件,如果采用回调函数的形式,很容易造成地狱回调,代码非常不容易进行维护. 因此,为了解决这个问题,有大神写了async这个中间件.极 ...

  8. (一)Nodejs - 框架类库 - Nodejs异步流程控制Async

    简介 Async是一个流程控制工具包,提供了直接而强大的异步功能 应用场景 业务流程逻辑复杂,适应异步编程,减少回调的嵌套 安装 npm insatll async 函数介绍 Collections ...

  9. async 异步流程控制规则

    github 学习async网址 : https://github.com/caolan/async/ 1.Async 函数介绍 async 主要实现了三个部分的流程控制功能 1.集合:Collect ...

随机推荐

  1. POJ 1141 Brackets Sequence(区间DP, DP打印路径)

    Description We give the following inductive definition of a “regular brackets” sequence: the empty s ...

  2. GIS-008-ArcGIS JS API 全图

    //待服务加载完成后,设置视野范围到全图范围 layer.on('load', function () { var extent = map.getLayer(map.layerIds[0]).ful ...

  3. Nginx(三)-- 配置文件之日志管理

    1.日志文件的默认存放位置 默认的日志文件存放位置在:nginx/logs/ 文件夹下,logs文件夹下有:access.log   error.log   nginx.pid 文件 2.nginx. ...

  4. ubuntu系统无eth0网卡解决办法

    ubuntu终端下命令ifconfig的问题解决 问题一. ifconfig之后只显示lo,没有看到eth0 问题二. ifconfig之后显示eth0,但是没有显示静态IP地址,即无inet.地址. ...

  5. 说说M451的例程库的说明

    Directory Information Document   Driver reference manual and reversion history. Library         Driv ...

  6. 说说SPI协议

    SPI,是英语Serial Peripheral Interface 的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管 ...

  7. iOS实现传递不定长的多个参数

    我们在使用苹果官方的文档的时候会发现可传不定数的参数例如: // [[UIAlertView alloc]initWithTitle:<#(nullable NSString *)#> m ...

  8. 利用border制作三角形原理

    网站前端页面中,有时候会使用一些三角形,除了使用图片的方式之外,利用css的border属性也可以做出相对应的三角形.那么,利用border是如何实现三角形的制作的呢? 先看下面一个例子: CSS代码 ...

  9. win8.1简单快速安装phpnow的方法

    工具/原料 phpnow 1.5.6 管理员身份登陆系统 方法/步骤   下载phpnow 这是必须的,大家可以自行百度下载,然后我们将phpnow放到一个文件夹,可以是根目录,也可以不是,但一定要知 ...

  10. ruby+gem常用命令

    gem是一种文件组织的包,一般的ruby的很多插件都有由这种各种的包提供.我们来看看gem的用法     ruby -v #查看ruby 版本 ruby -e ''require"watir ...