一、问题背景:
比如我们需要 按顺序获取:产品数据=>用户数据=>评论数据
传统的写法,无需解释
// 获取产品数据 ajax('products.json', (products) => { console.log('AJAX/products >>>', JSON.parse(products)); // 获取用户数据 ajax('users.json', (users) => { console.log('AJAX/users >>>', JSON.parse(users)); // 获取评论数据 ajax('products.json', (comments) => { console.log('AJAX/comments >>>', JSON.parse(comments)); }); }); });
二、强劲的新朋友 Generators
Generators 是 ES6 一个新的特性,能够 暂停/执行 代码。
yield 表示暂停且返回(return)其后面的值,
iterator.next 表示执行下一步
语法:
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); console.log(g.next()); // {value: 1, done: false} console.log(g.next()); // {value: 2, done: false} console.log(g.next()); // {value: 3, done: true }
语法:next( ) 带参数
function* gen() { while(true) { var value = yield 10; console.log(value); } } var g = gen(); g.next(1); // "{ value: 10, done: false }" g.next(2); // 2 // "{ value: 10, done: false }"
当程序第二次进入时,会把参数赋值给 value
但是返回值不变!
代码实现:
// Generators function request(url) { ajax(url, (response) => { gen.next(JSON.parse(response)); }); } function* main() { // 获取产品数据 let products = yield request('products.json'); // 获取用户数据 let users = yield request('users.json'); // 获取评论数据 let comments = yield request('comments.json'); console.log('Generator/products >>>', products); console.log('Generator/users >>>', users); console.log('Generator/comments >>>', comments); } var gen = main(); gen.next();
这个实现逻辑上看起来不是那么简介。尤其是 gen.next() 在函数内部、外部都在调用。
三、不算新的朋友 Promise
Promise 已经被提及已久了,是 ES6 的一部分。
Promise 能在写法上消除 callback 回调函数内的层层嵌套,相比起来代码更清晰了。
语法:
var promise1 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, 'foo'); }); console.log(promise1); // expected output: [object Promise] promise1.then(successMsg =>{ console.log(successMsg); }); // expected output: foo
resolve : 执行成功时的回调函数
reject:执行失败时的回调函数
function myAsyncFunction(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); }
let myFirstPromise = new Promise((resolve, reject) => { // do something // ... resolve("success!"); }); myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage); });
Promise的then()方法可以返回另一个Promise,也可以返回一个值。
如果返回的是一个值,它将会被包装成一个Promise。
代码实现如下:
// Promise // 封装 Ajax,返回一个 Promise function requestP(url, time) { return new Promise(function(resolve, reject) { setTimeout(()=>{resolve(url)}, time); }); } // 获取产品数据 requestP('products.json', 3000).then(function(products){ return products; //此处返回的是一个字符串,但会被包装成一个 Promise 对象。 }) // 获取用户数据 .then((products)=>{ console.log('Promises/products >>>', products); return requestP('users.json', 2000).then(function(users){ return users; }); }) // 获取评论数据 .then((users)=>{ console.log('Promises/users >>>', users); requestP('comments.json', 1000).then(function(comments){ console.log('Promises/comments >>>', comments); }); })
使用 Promise.all 可以更简洁。
注意:
Promise.all 只有所有promise都执行完成后,才会调用 then 函数。
Promise.all 不能保证执行的先后顺序,只能保证结果的有序返回。
// Promise // 封装 Ajax,返回一个 Promise function requestP(url, time) { return new Promise(function(resolve, reject) { setTimeout((time)=>{ console.log(`using ${time} ms.`); resolve(url); }, time, time); }); } Promise.all([ requestP('products.json', 3000), requestP('users.json', 2000), requestP('comments.json', 1000) ]) .then(function(data) { // 返回值 data 是一个数组:["xx", "xx", "xx"] console.log('Parallel promises >>>', data); }); /* 输出: using 1000 ms. using 2000 ms. using 3000 ms. Parallel promises >>> (3) ["products.json", "users.json", "comments.json"] */
再看下面的代码:
var promise1 = Promise.resolve(3); var promise2 = 42; var promise3 = new Promise(function(resolve, reject) { setTimeout((t)=>{ console.log(t); resolve(t);}, 3000, 'foo3'); }); var promise4 = new Promise(function(resolve, reject) { setTimeout((t)=>{ console.log(t); resolve(t);}, 2000, 'foo2'); }); var promise5 = new Promise(function(resolve, reject) { setTimeout((t)=>{ console.log(t); resolve(t);}, 1000, 'foo1'); }); Promise.all([promise1, promise2, promise3, promise4, promise5]).then(function(values) { console.log(values); }); /* expected output: > "foo1" > "foo2" > "foo3" > Array [3, 42, "foo3", "foo2", "foo1"] */
我们期望 foo3 应该在 foo1 的前面执行,而事实相反。
分析:
Promise.all 只能保证结果的输出顺序。
Promise.all 并不能保证 promise 的执行顺序,
四、 压轴出场的 async / await
如果要做到顺序执行,需要用到 async 函数,程式设计如下:
注意:await 的使用!
async function executeSequentially() { const tasks = [fn1, fn2, fn3] for (const fn of tasks) { await fn() } }
var tasks = [ _ => new Promise(res => setTimeout(_ => res("1"), 1000)), _ => new Promise(res => setTimeout(_ => res("2"), 1000)), _ => new Promise(res => setTimeout(_ => res("3"), 1000)), _ => new Promise(res => setTimeout(_ => res("4"), 1000)), _ => new Promise(res => setTimeout(_ => res("5"), 1000)), _ => new Promise(res => setTimeout(_ => res("6"), 1000)), _ => new Promise(res => setTimeout(_ => res("7"), 1000)) ]; (async (promises)=>{ for (let promise of promises) { console.log(await promise()); } })(tasks); /* Output: 1 2 3 4 5 6 7 */
思考:为什么不写成这种形式?
var tasks = [ new Promise(res => setTimeout(_ => res("1"), 1000)), new Promise(res => setTimeout(_ => res("2"), 1000)), new Promise(res => setTimeout(_ => res("3"), 1000)), new Promise(res => setTimeout(_ => res("4"), 1000)), new Promise(res => setTimeout(_ => res("5"), 1000)), new Promise(res => setTimeout(_ => res("6"), 1000)), new Promise(res => setTimeout(_ => res("7"), 1000)) ]; (async (promises)=>{ for (let promise of promises) { console.log(await promise); } })(tasks);
分析:
将 Promise 封装在一个函数中
_ => new Promise(res => setTimeout(_ => res("1"), 1000))
只有该函数执行时才会去 new Promise。
因为 Promise 一旦 new 出来后,便会自动执行。所以不要提前去 new。
语法:
1、async 函数返回一个 Promise 对象。return 的值,会成为 then 方法回调函数的参数。
async function f() { return 'hello world'; } f().then( (v) => console.log(v)) // hello world
2、async 函数会挨个等,等到内部所有 await 的 Promise 对象执行完,才会执行 then 方法。
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout)); async function f(){ await delay(1000); await delay(2000); await delay(3000); return 'done'; } f().then(v => console.log(v)); // 等待6s后才输出 'done'
3、正常情况下,await 命令后面跟一个 Promise 对象。如果不是的话,也会被转换成一个 立即 resolve 的 Promise 对象。
async function f() { return await 1; }; f().then( (v) => console.log(v)) // 1
4、如果 async 函数内部抛出异常,则返回的 Promise 对象状态为 reject 状态。异常可被 catch 方法回调函数接收到。
async function e(){ throw new Error('error'); } e().then(v => console.log(v)) .catch( e => console.log(e));
与 Promise 结合使用,await 实现线程阻塞,直至 Promise 执行结束。
// 模拟 Ajax,返回一个 Promise function requestP(url, time) { return new Promise(function(resolve, reject) { setTimeout((time)=>{ console.log(`using ${time} ms.`); resolve(url); }, time, time); }); } (async () => { // 获取产品数据 let products = await requestP('products.json', 3000); // 获取用户数据 let users = await requestP('users.json', 2000); // 获取评论数据 let comments = await requestP('comments.json', 1000); console.log('ES7 Async/products >>>', products); console.log('ES7 Async/users >>>', users); console.log('ES7 Async/comments >>>', comments); })(); /* Output: using 3000 ms. using 2000 ms. using 1000 ms. ES7 Async/products >>> products.json ES7 Async/users >>> users.json ES7 Async/comments >>> comments.json */
引用:
https://github.com/jaydson/es7-async
http://blog.csdn.net/sinat_17775997/article/details/60609498
http://blog.csdn.net/qq673318522/article/details/75331225
-