用ES7解决异步回调地狱问题
用了 Promise 其实并没有真正解决回调地狱问题,并且还新增了很多 .then(data => { .... }) 这些很没有意义的 “模板代码”。所以先人们又搞出了generator 和 async/await,generator 有一些很神奇的特性这个就不多说了,自己看 MDN 上的文档就好,但是 generator 也能挺方便地处理异步。
一、promise+迭代器=生成器
首先我们需要了解什么是迭代器( iterator),迭代就是指提供了统一的遍历复杂数据类型的方案。我们先回忆一下在内置对象中自带遍历器的有哪些?
- Array
- Set
- Map
- String
- NodeList
- 函数:arguments
使用迭代器的语法有(ES6):
- for-of
- ... 扩展运算符
function forOf(arr,deal){ //拿到迭代器函数 let iteratorFn = arr[Symbol.iterator]; //迭代器函数调用得到迭代器,(注意保护内部this指向) let it = iteratorFn.call(arr); // console.log(it.next());//next方法调用得到“结果对象”,结果对象有两个属性:value 当前遍历到的元素,done 指示遍历完成的标志(值为true/false) let resultObj; while (!(resultObj = it.next()).done) { console.log(resultObj.value); } } forOf(arr,function(v){ console.log(v); })
再来看看生成器:
- function* genFn(){},生成器函数:function*
- let gen = genFn();,调用生成器函数 得到生成器
- gen.next(),生成器函数的next方法,将生成器函数进行分段调用
补充:
- 结果对象,next()方法调用,不仅使函数分段调用了,还得到一个结果对象,包含value属性和done属性
- yield,生成器函数分段的关键字,后面可以跟一个value值,就是结果对象的value值
//生成器函数:function* yield 也可认为分段函数 function* genFn() { console.log(1111); yield 444; console.log(2222); yield 555; console.log(3333); } // //调用生成器函数 得到生成器 let gen = genFn(); console.log(gen.next()); console.log(gen.next()); console.log(gen.next()); let resultObj; while (!(resultObj = gen.next()).done) { console.log(resultObj.value); }
可以将yield看作分段,生成器函数中的的代码根据fn.next()调用次数来执行,调用几次则执行几段代码
二、ES7方法
1、关键字:
- async:“所有”函数都可以加上async关键字变成一个异步函数;
- await:只能使用在异步函数中,用来等待promise操作的promise.value
2、内容
- 异步函数执行的结果是一个Promise对象,该对象受到函数返回值的影响,具体参照Promise.resolve(value)
- 异步函数中的await关键字是有运算结果的,结果是一个Ptomise对象,该对象受到await后面的表达式影响,具体参照Promise.resolve(value)
现在有一个任务,有A,B,C,D四个步骤,其中D函数可以异步的得到一个结果,就是A函数想要的结果,怎么才能使A获得该结果?
思路:A---->B B---->C C---->D
首先我用比较粗暴的方法——回调函数来实现
function a(){ b(function(data){ console.log(data); }) } function b(cd_b){ c(function(data){ cd_b(data) }) } function c(cd_c){ d(function(data){ cd_c(data) }) } function d(cd_d){ setTimeout(()=>{ let data = "彩虹" cd_d(data) },2000) } a();
这种回调需要将每一次传参的弄个清楚,非常麻烦,而且很容易让人包括自己阅读起来昏头,所以不建议使用这种方法,那么如果我们使用ES7的方法是否能简化代码,使其语义化增强呢?
async function A() { console.log(await B()); } async function B() { return await C(); } async function C() { return await D(); } async function D() { return await new Promise((resolve) => { setTimeout(() => { let data = "彩虹" resolve(data); }, 3000) }) } A();
可以看到,我们只需要将返回值获取,调用最顶层的函数就可以了,语义化比回调函数强上很多,也不存在传参混乱的问题了,这是一个非常方便的方法。