diff --git a/docs/let.md b/docs/let.md index cad5163..3730135 100644 --- a/docs/let.md +++ b/docs/let.md @@ -25,7 +25,7 @@ for循环的计数器,就很合适使用let命令。 ```javascript for(let i = 0; i < arr.length; i++){} - + console.log(i) //ReferenceError: i is not defined @@ -80,9 +80,9 @@ function do_something() { ```javascript -if (1) { - typeof x; // ReferenceError - let x; +if (1) { + typeof x; // ReferenceError + let x; } ``` @@ -93,16 +93,16 @@ if (1) { ```javascript -var tmp = 123; +var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError - let tmp; + let tmp; } ``` -上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。 +上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。 ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些命令,就会报错。 @@ -114,10 +114,10 @@ if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError - + let tmp; // TDZ结束 console.log(tmp); // undefined - + tmp = 123; console.log(tmp); // 123 } diff --git a/docs/object.md b/docs/object.md index 81e89a1..838f0d3 100644 --- a/docs/object.md +++ b/docs/object.md @@ -276,14 +276,14 @@ function clone(origin) { 将多个对象合并到某个对象。 ```javascript -const merge = +const merge = (target, ...sources) => Object.assign(target, ...sources); ``` 如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。 ```javascript -const merge = +const merge = (...sources) => Object.assign({}, ...sources); ``` @@ -516,7 +516,7 @@ Symbol.for方法在全局环境中搜索指定key的Symbol值,如果存在就 ```javascript -Symbol.for("bar") === Symbol.for("bar") +Symbol.for("bar") === Symbol.for("bar") // true Symbol("bar") === Symbol("bar") @@ -530,7 +530,7 @@ Symbol.keyFor方法返回一个已登记的Symbol类型值的key。 ```javascript -var s1 = Symbol.for("foo"); +var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" var s2 = Symbol("foo"); @@ -741,8 +741,8 @@ var pipe = (function () { return new Proxy({}, { get: function (pipeObject, fnName) { if (fnName == "get") { - return pipe.reduce(function (val, fn) { - return fn(val); + return pipe.reduce(function (val, fn) { + return fn(val); }, value); } pipe.push(window[fnName]); @@ -752,12 +752,12 @@ var pipe = (function () { } }()); -var double = function (n) { return n*2 }; +var double = function (n) { return n*2 }; var pow = function (n) { return n*n }; var reverseInt = function (n) { return n.toString().split('').reverse().join('')|0 }; -pipe(3) . double . pow . reverseInt . get -// 63 +pipe(3) . double . pow . reverseInt . get +// 63 ``` @@ -855,7 +855,7 @@ let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123; proxy.foo // 123 - + revoke(); proxy.foo // TypeError: Revoked @@ -870,9 +870,9 @@ Object.observe方法用来监听对象(以及数组)的变化。一旦监听 ```javascript var user = {}; -Object.observe(user, function(changes){ +Object.observe(user, function(changes){ changes.forEach(function(change) { - user.fullName = user.firstName+" "+user.lastName; + user.fullName = user.firstName+" "+user.lastName; }); }); @@ -890,9 +890,9 @@ user.fullName // 'Michael Jackson' var div = $("#foo"); -Object.observe(user, function(changes){ +Object.observe(user, function(changes){ changes.forEach(function(change) { - var fullName = user.firstName+" "+user.lastName; + var fullName = user.firstName+" "+user.lastName; div.text(fullName); }); }); @@ -908,12 +908,12 @@ Object.observe(user, function(changes){ var o = {}; function observer(changes){ - changes.forEach(function(change) { - console.log('发生变动的属性:' + change.name); - console.log('变动前的值:' + change.oldValue); - console.log('变动后的值:' + change.object[change.name]); - console.log('变动类型:' + change.type); - }); + changes.forEach(function(change) { + console.log('发生变动的属性:' + change.name); + console.log('变动前的值:' + change.oldValue); + console.log('变动后的值:' + change.object[change.name]); + console.log('变动类型:' + change.type); + }); } Object.observe(o, observer); @@ -925,9 +925,9 @@ Object.observe(o, observer); ```javascript var change = { - object: {...}, - type: 'update', - name: 'p2', + object: {...}, + type: 'update', + name: 'p2', oldValue: 'Property 2' } diff --git a/docs/promise.md b/docs/promise.md index 9cc41b3..110a1fb 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -366,6 +366,20 @@ p.then(function (s){ 上面代码生成一个新的Promise对象的实例p,它的状态为fulfilled,所以回调函数会立即执行,Promise.resolve方法的参数就是回调函数的参数。 +所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法。 + +```javascript + +var p = Promise.resolve(); + +p.then(function () { + // ... +}); + +``` + +上面代码的变量p就是一个Promise对象。 + 如果Promise.resolve方法的参数是一个Promise对象的实例,则会被原封不动地返回。 Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。 @@ -430,9 +444,7 @@ run(g); ### 概述 -async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的另一种方法。它可以写出比Promise和Generator更简洁易读的代码,但是依赖这两者来实现。 - -async函数并不属于ES6,而是被列入了ES7,但是traceur编译器和regenerator转码器已经实现了这个功能。 +async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的另一种方法。它可以写出比Promise和Generator更简洁易读的代码,但是依赖这两者来实现。async函数并不属于ES6,而是被列入了ES7,但是traceur、Babel.js、regenerator等转码器已经支持这个功能,转码后立刻就能使用。 在用法上,只要函数名之前加上async关键字,就表明该函数内部有异步操作。该异步操作应该返回一个Promise对象,前面用await关键字注明。当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。 @@ -453,6 +465,8 @@ getStockPrice("JNJ") 上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数将返回一个Promise对象。调用该函数时,当遇到await关键字,立即返回它后面的表达式(getStockPrice函数)产生的Promise对象,不再执行函数体内后面的语句。等到getStockPrice完成,再自动回到函数体内,执行剩下的语句。 +仔细观察getStockPrice函数,你会发现除了添加async、await这两个命令,整个代码与同步操作的流程一模一样。这就是async函数的本意,尽可能地用同步的流程,表达异步操作。 + 下面是一个更一般性的例子。 ```javascript @@ -472,6 +486,91 @@ async function asyncValue(value) { 上面代码中,asyncValue函数前面有async关键字,表明函数体内有异步操作。执行的时候,遇到await语句就会先返回,等到timeout函数执行完毕,再返回value。 +总之,async函数的关键就是,await命令后面,必须是一个返回Promise对象的操作。因为Promise对象的运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。 + +```javascript + +async function myFunction() { + try { + await somethingThatReturnsAPromise(); + } catch (err) { + console.log(err); + } +} + +``` + +await命令只能用在async函数之中,如果用在普通函数,就会报错。 + +```javascript + +async function dbFuc(db) { + let docs = [{}, {}, {}]; + + // 报错 + docs.forEach(function (doc) { + await db.post(doc); + }); +} + +``` + +上面代码会报错,因为await用在普通函数之中了。但是,如果将forEach方法的参数改成async函数,也有问题。 + +```javascript + +async function dbFuc(db) { + let docs = [{}, {}, {}]; + + // 可能得到错误结果 + docs.forEach(async function (doc) { + await db.post(doc); + }); +} + +``` + +上面代码可能不会正常工作,原因是这时三个db.post操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用for循环。 + +```javascript + +async function dbFuc(db) { + let docs = [{}, {}, {}]; + + for (let doc of docs) { + await db.post(doc); + } +} + +``` + +如果确实希望多个请求并发执行,可以使用Promise.all方法。 + +```javascript + +async function dbFuc(db) { + let docs = [{}, {}, {}]; + let promises = docs.map((doc) => db.post(doc)); + + let results = await Promise.all(promises); + console.log(results); +} + +// 或者使用下面的写法 + +async function dbFuc(db) { + let docs = [{}, {}, {}]; + let promises = docs.map((doc) => db.post(doc)); + + let results = []; + for (let promise of promises) { + results.push(await promise); + } + console.log(results); +} + +``` + ES6将await增加为保留字。使用这个词作为标识符,在ES5是合法的,在ES6将抛出SyntaxError。 ### 与Promise、Generator的比较 diff --git a/docs/reference.md b/docs/reference.md index 37d3935..86acfe6 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -87,6 +87,7 @@ - Jack Franklin, [Embracing Promises in JavaScript](http://javascriptplayground.com/blog/2015/02/promises/): catch方法的例子 - Luke Hoban, [Async Functions for ECMAScript](https://github.com/lukehoban/ecmascript-asyncawait): Async函数的设计思想,与Promise、Gernerator函数的关系 - Jafar Husain, [Asynchronous Generators for ES7](https://github.com/jhusain/asyncgenerator): Async函数的深入讨论 +- Nolan Lawson, [Taming the asynchronous beast with ES7](http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html): async函数通俗的实例讲解 ## Class与模块