使用生成器函数和Promise改善异步流程

最近在学习JavaScript异步相关的知识,在学到Generator(生成器函数)、Promise 和 async-awiat 等相关对异步优化的内容时遇到了一些一时没有理解的东西。本文仅作为学习过程中的笔记分享,第一次发文章,如果错误请不吝指出。

话不多说,进入正题,先写一下在 Generator 中要用到的函数,随便写几个就行。

function getValue(val, duration){
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(val);
        }, duration);
    })
}

function delay(duration){
    return new Promise(resolve => {//delay函数不resolve出任何东西,或者说resolve出undefined
        setTimeout(resolve, duration);
    })
}

function getJSON(url){
    return get(url).then(JSON.parse);
}

然后是 Generator 函数主体

function *f(){
    var data = yield getJSON('data.json')//(1)
    yield delay(1000);//(2)
    var b = yield getValue(10, 3000);//(3)
}

Generator 函数会在遇到 yield 时生成一个值并暂停执行,如果要继续执行,则要调用next(),yield 的运算结果是生成器 next() 里面的参数。

为了执行 Generator函数 f ,声明一个函数 g 作为函数 f 的调用。

var g = f();

g.next().value.then(value =>{
  g.next(value).value.then(value =>
    g.next(value).value.then(value =>
      g.next(value);
        });
    });
});

分析一下对 g 调用 next() 的过程。

在声明完函数 g 之后。第一行的第一个 value 是 getJSON 解析好的的 promise(我们希望在 promise 在 resolve 以后恢复执行),然后通过 data 返回,但是我们也不确定什么时候能resolve,但是可以确定的是:resolve 之后恢复生成器的执行。

第二行的第一个 value 是上一个 then 传入的 value(getJSON(‘data.json’) resolve 的结果),第二个 value 是 delay(1000) 的 promise 的 resolve。

接下来是第三行,很明显,next() 调用一次就恢复执行,然后暂停,再调一次就再恢复执行,这里是第三次调用,传入的 value(第二个 value)是第(2)行的返回结果,虽然 delay 并没啥返回(甚至左边没有变量接住)。这次调用将会返回第(3)行生成的 promise 对象,然后等getValue 函数 resolve 以后,再恢复执行。这时这里第三个 value 应该是10并返回给b。

最后一行,即第四行 g.next(value); 这里的 value 为10。





总结一下 f() 函数的执行

运行到第(1)行的 yield 时,在 yield 出 getJSON 的 promise 后,它执行停止了,直到 promise 的 resolve 后才恢复执行,并且恢复之后还将返回 promise 的 resolve 的值,也就是 data 将等于 data.json 解析出的内容。

然后函数运行到第(2)行,又 yield 出了 delay 的 promise,并再次等待 promise 的 resolve后才恢复执行(虽然这里的 delay 本身没求值结果所以也不 resolve 出啥),delay 在1秒后完成,所以第(2)行的 yield 也在1秒后恢复。

运行到第(3)行,又 yield 出一个promise,然后就停住了,在等待这个 promise 的 resolve 后才恢复,要恢复得调用 next(),而 next 的调用在 promise 的 resolve之后,即这时已经有resolve 的值了,把值传给 next() 即可,b 就等于这个 resolve 出的值。全程 next() 调用在那行的 promise 的 resolve之后,而且调用的时候把 promise 的 resovle 的值传给 next() 并成为yield 的返回值。

f() 这样跟同步的写法区别仅在于有没有 yield,虽然是异步的,但是写在了同一行里。(1)行的data 最终将等于 data.json 用 getJSON 请求到的真正的数据,而不是一个 promise 对象。虽然看起来像同步,但实际上代码在运行到这行的时候暂停了一定的时间,时间取决于 promise 的 resolve 的时间,在等待的这段时间,浏览器是完全空闲的。

在以上的举例分析中,我们只考虑了 promise 能正确 resolve 的情况,那么接下来再写一下出现错误的情况(拿 next() 调用 g 的第一行举例)

g.next().value.then(value =>{
    g.next(value);//成功就通过yield返回
}, reason =>{
    g.throw(reason);//失败就通过yield抛出
});

console.log(1);

如果成功的就调用这个箭头函数(第一个 value 是 promise),then 只是挂上了两个并没有立刻得到结果的函数就结束,然后执行下一句(console.log(1);)了,注意即使在then调用时promise 已经得出结果,下一句 console.log(1); 仍然先于then里的函数执行。因为这个函数是异步调用的,里面的内容必定在 then 所在的调用栈全部清空以后执行,

另外说一下,既然有错误,就应该加上 try-catch 语句。

try{
    var data = yield getJSON('data.json')
}catch(e){
    console.log(e);
    return;
 }
 //多了yield的好处:能看出代码哪里需要等待

最后总结和补充一下 Generator 和 async-await 对于异步优化的一些知识点

  • Generator可以让执行处于暂停状态,Generator 返回的是一个 Iterator 对象。
  • 必要构成,1 个*号和 yield 运算符。
  • next 属性返回一个对象,里面 value 是当前 yield 后面的值,done 表示当前生成器有没有运行完。yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。yield可以生成一个值,然后恢复的话(也可以不恢复,就不返回值)又可以返回一个值,两个值可以不一样。
  • yield: yield 的运算结果是生成器 next() 里面的参数。
  • async + 生成器函数 function {await :promise 函数}。 async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await。
  • async 函数对 Generator 函数的改进,配合 promise 使用(后面接返回promise的函数?),包装原理如上。并且 function() 返回一个 promise。async-await 本质是生成器函数和yield。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值