From d51beedbd61a99ee310676b62d2fa1bba820afb8 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 28 Mar 2017 19:46:30 +0800 Subject: [PATCH] docs: edit number.isFinite --- docs/async.md | 41 +++++++++++++++++++++++++++++++++++++- docs/generator-async.md | 44 ++++++++++++++++++++++++++++++++++++++--- docs/intro.md | 6 +++--- docs/number.md | 2 +- docs/reflect.md | 26 ++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/docs/async.md b/docs/async.md index de1bc59..ce5885e 100644 --- a/docs/async.md +++ b/docs/async.md @@ -100,7 +100,7 @@ function timeout(ms) { async function asyncPrint(value, ms) { await timeout(ms); - console.log(value) + console.log(value); } asyncPrint('hello world', 50); @@ -108,6 +108,23 @@ asyncPrint('hello world', 50); 上面代码指定50毫秒以后,输出`hello world`。 +由于`async`函数返回的是 Promise 对象,可以作为`await`命令的参数。所以,上面的例子也可以写成下面的形式。 + +```javascript +async function timeout(ms) { + await new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +async function asyncPrint(value, ms) { + await timeout(ms); + console.log(value); +} + +asyncPrint('hello world', 50); +``` + async 函数有多种使用形式。 ```javascript @@ -315,6 +332,28 @@ async function main() { } ``` +下面的例子使用`try...catch`结构,实现多次重复尝试。 + +```javascript +const superagent = require('superagent'); +const NUM_RETRIES = 3; + +async function test() { + let i; + for (i = 0; i < NUM_RETRIES; ++i) { + try { + await superagent.get('http://google.com/this-throws-an-error'); + break; + } catch(err) {} + } + console.log(i); // 3 +} + +test(); +``` + +上面代码中,如果`await`操作成功,就会使用`break`语句退出循环;如果失败,会被`catch`语句捕捉,然后进入下一轮循环。 + ### 使用注意点 第一点,前面已经说过,`await`命令后面的`Promise`对象,运行结果可能是`rejected`,所以最好把`await`命令放在`try...catch`代码块中。 diff --git a/docs/generator-async.md b/docs/generator-async.md index 7993997..4018cd1 100644 --- a/docs/generator-async.md +++ b/docs/generator-async.md @@ -544,9 +544,9 @@ var co = require('co'); co(gen); ``` -上面代码中,Generator函数只要传入`co`函数,就会自动执行。 +上面代码中,Generator 函数只要传入`co`函数,就会自动执行。 -`co`函数返回一个Promise对象,因此可以用`then`方法添加回调函数。 +`co`函数返回一个`Promise`对象,因此可以用`then`方法添加回调函数。 ```javascript co(gen).then(function (){ @@ -568,7 +568,7 @@ co(gen).then(function (){ (2)Promise 对象。将异步操作包装成 Promise 对象,用`then`方法交回执行权。 -co 模块其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个模块。使用 co 的前提条件是,Generator 函数的`yield`命令后面,只能是 Thunk 函数或 Promise 对象。如果数组或对象的成员,全部都是 Promise 对象,也是可以的,详见后文的例子。(4.0 版以后,`yield`命令后面只能是 Promise 对象。) +co 模块其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个模块。使用 co 的前提条件是,Generator 函数的`yield`命令后面,只能是 Thunk 函数或 Promise 对象。如果数组或对象的成员,全部都是 Promise 对象,也可以使用 co,详见后文的例子。(co v4.0版以后,`yield`命令后面只能是 Promise 对象,不再支持 Thunk 函数。) 上一节已经介绍了基于 Thunk 函数的自动执行器。下面来看,基于 Promise 对象的自动执行器。这是理解 co 模块必须的。 @@ -752,3 +752,41 @@ function* somethingAsync(x) { 上面的代码允许并发三个`somethingAsync`异步操作,等到它们全部完成,才会进行下一步。 +### 实例:处理 Stream + +Node 提供 Stream 模式读写数据,特点是一次只处理数据的一部分,数据分成一块块依次处理,就好像“数据流”一样。这对于处理大规模数据非常有利。Stream 模式使用 EventEmitter API,会释放三个事件。 + +- `data`事件:下一块数据块已经准备好了。 +- `end`事件:整个“数据流”处理“完了。 +- `error`事件:发生错误。 + +使用`Promise.race()`函数,可以判断这三个事件之中哪一个最先发生,只有当`data`时间最先发生时,才进入下一个数据块的处理。从而,通过一个`while`循环,完成所有数据的读取。 + +```javascript +const co = require('co'); +const fs = require('fs'); + +const stream = fs.createReadStream('./les_miserables.txt'); +let valjeanCount = 0; + +co(function*() { + while(true) { + const res = yield Promise.race([ + new Promise(resolve => stream.once('data', resolve)), + new Promise(resolve => stream.once('end', resolve)), + new Promise((resolve, reject) => stream.once('error', reject)) + ]); + if (!res) { + break; + } + stream.removeAllListeners('data'); + stream.removeAllListeners('end'); + stream.removeAllListeners('error'); + valjeanCount += (res.toString().match(/valjean/ig) || []).length; + } + console.log('count:', valjeanCount); // count: 1120 +}); +``` + +上面代码采用 Stream 模式读取《悲惨世界》的文本文件,对于每个数据块都使用`stream.once`方法,在`data`、`end`、`error`三个事件上添加一次性回调函数。变量`res`只有在`data`事件发生时,才有值。然后,累加每个数据块之中`valjean`这个词出现的次数。 + diff --git a/docs/intro.md b/docs/intro.md index e6c9c14..77412e7 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -48,9 +48,9 @@ ES6 的第一个版本,就这样在2015年6月发布了,正式名称就是 ES6 从开始制定到最后发布,整整用了15年。 -前面提到,ECMAScript 1.0是1997年发布的,接下来的两年,连续发布了 ECMAScript 2.0(1998年6月)和 ECMAScript 3.0(1999年12月)。3.0版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习 JavaScript,其实就是在学3.0版的语法。 +前面提到,ECMAScript 1.0 是1997年发布的,接下来的两年,连续发布了 ECMAScript 2.0(1998年6月)和 ECMAScript 3.0(1999年12月)。3.0版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习 JavaScript,其实就是在学3.0版的语法。 -2000年,ECMAScript 4.0开始酝酿。这个版本最后没有通过,但是它的大部分内容被 ES6 继承了。因此,ES6 制定的起点其实是2000年。 +2000年,ECMAScript 4.0 开始酝酿。这个版本最后没有通过,但是它的大部分内容被 ES6 继承了。因此,ES6 制定的起点其实是2000年。 为什么 ES4 没有通过呢?因为这个版本太激进了,对 ES3 做了彻底升级,导致标准委员会的一些成员不愿意接受。ECMA 的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订 ECMAScript 标准,成员包括 Microsoft、Mozilla、Google 等大公司。 @@ -72,7 +72,7 @@ ES6 从开始制定到最后发布,整整用了15年。 各大浏览器的最新版本,对 ES6 的支持可以查看[kangax.github.io/es5-compat-table/es6/](http://kangax.github.io/es5-compat-table/es6/)。随着时间的推移,支持度已经越来越高了,超过90%的 ES6 语法特性都实现了。 -Node 是 JavaScript 的服务器运行环境(runtime)。它对 ES6 的支持更高。除了那些默认打开的功能,还有一些语法功能已经实现了,但是默认没有打开。使用下面的命令,可以查看 Node 那些没有打开的 ES6 特性。 +Node 是 JavaScript 的服务器运行环境(runtime)。它对 ES6 的支持度更高。除了那些默认打开的功能,还有一些语法功能已经实现了,但是默认没有打开。使用下面的命令,可以查看 Node 已经实现的 ES6 特性。 ```bash $ node --v8-options | grep harmony diff --git a/docs/number.md b/docs/number.md index e4e72ea..e10c0bd 100644 --- a/docs/number.md +++ b/docs/number.md @@ -94,7 +94,7 @@ ES5通过下面的代码,部署`Number.isNaN()`。 })(this); ``` -它们与传统的全局方法`isFinite()`和`isNaN()`的区别在于,传统方法先调用`Number()`将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,isFinite对于非数值一律返回`false`, isNaN只有对于NaN才返回`true`,非NaN一律返回`false`。 +它们与传统的全局方法`isFinite()`和`isNaN()`的区别在于,传统方法先调用`Number()`将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,`Number.isFinite()`对于非数值一律返回`false`, `Number.isNaN()`只有对于`NaN`才返回`true`,非`NaN`一律返回`false`。 ```javascript isFinite(25) // true diff --git a/docs/reflect.md b/docs/reflect.md index ed9b028..3490201 100644 --- a/docs/reflect.md +++ b/docs/reflect.md @@ -193,6 +193,32 @@ Reflect.set(1, 'foo', {}) // 报错 Reflect.set(false, 'foo', {}) // 报错 ``` +注意,`Reflect.set`会触发`Proxy.defineProperty`拦截。 + +```javascript +let p = { + a: 'a' +}; + +let handler = { + set(target,key,value,receiver) { + console.log('set'); + Reflect.set(target,key,value,receiver) + }, + defineProperty(target, key, attribute) { + console.log('defineProperty'); + Reflect.defineProperty(target,key,attribute); + } +}; + +let obj = new Proxy(p, handler); +obj.a = 'A'; +// set +// defineProperty +``` + +上面代码中,`Proxy.set`拦截中使用了`Reflect.set`,导致触发`Proxy.defineProperty`拦截。 + ### Reflect.has(obj, name) `Reflect.has`方法对应`name in obj`里面的`in`运算符。