资讯专栏INFORMATION COLUMN

前端异步解决方案-4.1(generator)

XUI / 1026人阅读

摘要:前言终于开始写了,离这个系列的终结又进了一步。想要看更深度解析的朋友可以移步漫话与异步第三话化异步为同步这里面谈及了的底层实现及的用法。用法在我的理解中,最大的特点就是可以让函数在特定的地方停下,等待被唤醒后在函数内部环境中继续执行。

前言

终于开始写generator了,离这个系列的终结又进了一步。其实generator我还处在会用但是不理解原理的状态,但是知识不总结,不记录的话容易忘记,所以我还是把现在的一点心得记录下来。等到以后有了更深的理解再回来补充。
想要看更深度解析generator的朋友可以移步漫话JavaScript与异步·第三话——Generator:化异步为同步这里面谈及了generator的底层实现及generator的用法。是我看过的文章中自认为解释的最好的一篇,而且篇幅也不长,建议大家去看一看。

实现

根据一贯的作风,我们先尝试自己实现generator
尝试ing............
好了尝试完了,实现不了,老老实实的学习generator的用法吧。

用法

在我的理解中,generator最大的特点就是可以让函数在特定的地方停下,等待被唤醒后在函数内部环境中继续执行。我们结合代码来看一看:
注释:【1】Iterator Object对象:参考 Iterator 文章比较长,但是如果只是想要了解什么是Iterator Object的话看完第一小节就足够了

//输出分割线的函数,感兴趣的可以自行百度如何设置console.log的样式
function cut_off(color) {
  console.log("%c------------------------------------------","color:"+color+";font-size:20px");
}

//* 为generator函数的标识,如果我们想要创建一个generator函数就必须在function后面加上*
function* generator() {
  let num1, num2;
  num1 = 123;
  console.log("num1", num1, "num2", num2);
  //yield就是该函数内部暂停的地方,暂停的同时会把yield后面的值返回出去
  yield num1;
  num2 = 456;
  console.log("num1", num1, "num2", num2);
  yield num2;
  console.log("num1", num1, "num2", num2);
  return "end"
}

console.log("generator defined");
//函数返回一个Iterator Object对象;
// 但是与普通函数不同的是,这个时候函数并不执行函数内部的代码
let g = generator();
console.log("g defined");
cut_off("red");

console.log("g.next() run 1");
//开始执行函数内部的代码,并且遇在到yield的时候返回 yield后面的值
console.log(g.next());
cut_off("red");

console.log("g.next() run 2");
//从上次执行完的地方执行,并且遇在到yield的时候返回 yield后面的值
console.log(g.next());
cut_off("red");

console.log("g.next() run 3");
//从上次执行完的地方执行,这次是最后一次有值的返回,done的状态会变为true
console.log(g.next());
cut_off("red");

console.log("g.next() run 4");
//已经执行完成之后再次被调用,永远返回{value:undefined, done: true}
console.log(g.next());
cut_off("red");

console.log("g.next() run 5");
//已经执行完成之后再次被调用,永远返回{value:undefined, done: true}
console.log(g.next());

贴上一张代码和运行结果的对比图辅助大家理解

希望大家看到这里已经理解了generator的基本用法,但是这个东西确实有点难,所以我放出联系方式
我有空的时候可以一起探讨一下

接下来我要讲generator最重要的第二个特性,我们可以通过.next(value)yeild赋值(这是不准确的说法,但是我们可以这么理解,方便我们使用generator),还是贴代码:

function* generator() {
  //第一次调用.next()是启动了这个函数一直运行到下一个yield的位置
  let num1, num2;
  num1 = 123;
  console.log("num1", num1, "num2", num2);
  num2 = yield num1;/*1.由于运算的顺序,js会先计算右边的值,也就是在num2被赋值之前,函数就停止运行了
                      2.第二次调用.next(value)的时候,value被next传入了yield的位置
                      3.程序继续运行,value被赋值给了num2
                     */
  console.log("num1", num1, "num2", num2);
  return num2
}

let g = generator();

console.log("g.next() run 1");
console.log(g.next());
cut_off("red");

console.log("g.next(789) run 2");
//从上次执行完的地方执行,并且将789传入函数内部
console.log(g.next(789));

贴出函数和运行结果的对比图,辅助大家理解.next(value)为yeild赋值(该说法并不准确)

generator的基础学习到这里就能尝试实际使用了,接下来我们尝试让generator在异步中大展身手吧,话不多说上代码;

//模拟异步请求
let request = function (sucF, errF) {
  if ((sucF && typeof sucF !== "function") || (errF && typeof errF !== "function")) {
    throw new Error("传入参数必须为函数")
  }
  setTimeout(function () {
    let data = parseInt(Math.random() * 100);
    if (data < 90 && sucF) {
      sucF(data)
    } else if (errF) {
      errF("本次异步失败了")
    }
  }, 100)
};

function* generator() {
  let num1, num2;
  num1 = yield request((data) => {
    g.next(data)                 //这个地方可能有些难以理解,为什么在g被创建之前,就被使用了。
  }, err => {                   //这个问题可以看注释【1】来辅助理解
    console.error(err);
    g.return(err)               //跳过所有的yield,return直接返回err
  });
  console.log("num1", num1, "num2", num2);
  num2 = yield  request((data) => {
    g.next(data)
  }, err => {
    console.error(err);
    g.return(err)             //跳过所有的yield,return直接返回err
  });
  console.log("num1", num1, "num2", num2);
  return num2
}

let g = generator();
//启动
g.next();

注释【1】:为了解释g为什么可以在被创建之前就‘被调用’

//其实在运行generator的时候generator内部的代码块并没有开始运行,而是返回了一个Iterator对象,所以大家可以这样简单理解
//function* generator 相当于 
function generator(){
    let obj = {};
    obj.next=function(){
        console.log(g);
    };
    return obj
}
//这样在调用generator()的时候,g不会被使用
let g = generator();
//这个时候g被调用了,但是在上一行代码中,g已经被定义了,所以没有问题,不会报错
g.next()

这篇文章暂时写到这里,因为我的页面开始卡了,所以我把generator和promise结合的用法放到下一篇文章中去;(ps:今天就会写出来的)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103633.html

相关文章

  • [前端漫谈_2] 从 Dva 的 Effect 到 Generator + Promise 实现异步

    摘要:你能学到什么如何使用实现异步编程异步编程的原理解析前言结合上一篇文章,我们来聊聊基础原理说到异步编程,你想到的是和,但那也只是的语法糖而已。表示一个异步操作的最终状态完成或失败,以及其返回的值。至此实现异步操作的控制。 你能学到什么 如何使用 Generator + Promise 实现异步编程 异步编程的原理解析 前言 结合 上一篇文章 ,我们来聊聊 Generator 基础原理...

    pekonchan 评论0 收藏0
  • 一篇文章了解前端异步编程方案演变

    摘要:对于而言,异步编程我们可以采用回调函数,事件监听,发布订阅等方案,在之后,又新添了,,的方案。总结本文阐述了从回调函数到的演变历史。参考文档深入掌握异步编程系列理解的 对于JS而言,异步编程我们可以采用回调函数,事件监听,发布订阅等方案,在ES6之后,又新添了Promise,Genertor,Async/Await的方案。本文将阐述从回调函数到Async/Await的演变历史,以及它们...

    lmxdawn 评论0 收藏0
  • JavaScript异步操作(续)

    摘要:环境中产生异步操作的函数分为两大类计时函数和函数。如果要在应用中定义复杂的异步操作,就要使用者两类异步函数作为基本的构造快。在本例子中同步事件循环不包含内部的外部的异步事件循环内部的。其弊端是操作强耦合维护代价高。 JavaScript环境中产生异步操作的函数分为两大类:计时函数和I/O函数。如果要在应用中定义复杂的异步操作,就要使用者两类异步函数作为基本的构造快。本文没有对某个知识点...

    Travis 评论0 收藏0
  • 细说JS异步发展历程

    摘要:换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。参考文章珠峰架构课墙裂推荐细说异步函数发展历程异步编程谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和,您的肯定是我前进的最大动力。知其然知其所以然,首先了解三个概念: 1.什么是同步? 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了...

    Alfred 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<