前端异步解决方案大全(2021版)(三)

简介: 前端异步解决方案大全(2021版)

2、gengerator函数


在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。


es6 提供的 generator函数。


总得来说就三点:


*在function关键字后加一个* , 那么这个函数就称之为generator函数

*函数体有关键字 yield , 后面跟每一个任务 , 也可以有return关键字, 保留一个数据

*通过next函数调用, 几个调用, 就是几个人任务执行


简单使用

Generator的声明方式类似一般的函数声明,只是多了个*号,并且一般可以在函数内看到yield关键字。


function* showWords() {
    yield 'one';
    yield 'two';
    return 'three';
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: "two"}
show.next() // {done: true, value: "three"}
show.next() // {value: underfined, done: true}

如上代码,定义了一个showWords的生成器函数,调用之后返回了一个迭代器对象(即show)。


调用next方法后,函数内执行第一条yield语句,输出当前的状态done(迭代器是否遍历完成)以及相应值(一般为yield关键字后面的运算结果)。


每调用一次next,则执行一次yield语句,并在该处暂停,return完成之后,就退出了生成器函数,后续如果还有yield操作就不再执行了。


当然还有以下情况:(next()数量小于yield)

function* g1(){
  yield '任务1'
  yield '任务2'
  yield '任务3'
  return '任务4'
}
const g1done = g1()
console.log(g1done.next()) //{ value: '任务1', done: false }
console.log(g1done.next()) //{ value: '任务2', done: false }

加上yield和yield*

有时候,我们会看到yield之后跟了一个*号,它是什么,有什么用呢?

类似于生成器前面的*号,yield后面的星号也跟生成器有关,举个大栗子:

function* showWords() {
    yield 'one';
    yield showNumbers();
    return 'three';
}
function* showNumbers() {
    yield 10 + 1;
    yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: showNumbers}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}

增添了一个生成器函数,我们想在showWords中调用一次,简单的 yield showNumbers()之后发现并没有执行函数里面的yield 10+1。


因为yield只能原封不动地返回右边运算后值,但现在的showNumbers()不是一般的函数调用,返回的是迭代器对象。


所以换个yield* 让它自动遍历进该对象:

function* showWords() {
    yield 'one';
    yield* showNumbers();
    return 'three';
}
function* showNumbers() {
    yield 10 + 1;
    yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: 11}
show.next() // {done: false, value: 12}
show.next() // {done: true, value: "three"}

要注意的是,这yield和yield* 只能在generator函数内部使用,一般的函数内使用会报错。

1. function showWords() {
2. yield 'one'; // Uncaught SyntaxError: Unexpected string
3. }

虽然换成yield*不会直接报错,但使用的时候还是会有问题,因为’one'字符串中没有Iterator接口,没有yield提供遍历:


function showWords() {
    yield* 'one'; 
}
var show = showWords();
show.next() // Uncaught ReferenceError: yield is not defined

在开发中,我们常常需要请求多个地址,为了保证顺序,引入Promise对象和Generator生成器函数,看这个简单的栗子:

var urls = ['url1', 'url2', 'url3'];
function* request(urls) {
    urls.forEach(function(url) {
        yield req(url);
    });
//     for (var i = 0, j = urls.length; i < j; ++i) {
//         yield req(urls[i]);
//     }
}
var r = request(urls);
r.next();
function req(url) {
    var p = new Promise(function(resolve, reject) {
        $.get(url, function(rs) {
            resolve(rs);
        });
    });
    p.then(function() {
        r.next();
    }).catch(function() {
    });
}

上述代码中forEach遍历url数组,匿名函数内部不能使用yield关键字,改换成注释中的for循环就行了。

next()调用中的传参

参数值有注入的功能,可改变上一个yield的返回值,如:

function* showNumbers() {
    var one = yield 1;
    var two = yield 2 * one;
    yield 3 * two;
}
var show = showNumbers();
show.next().value // 1
show.next().value // NaN
show.next(2).value // 6

第一次调用next之后返回值one为1,但在第二次调用next的时候one其实是undefined的,因为generator不会自动保存相应变量值,我们需要手动的指定,这时two值为NaN,在第三次调用next的时候执行到yield 3 * two,通过传参将上次yield返回值two设为2,得到结果。


另一个栗子:


由于ajax请求涉及到网络,不好处理,这里用了setTimeout模拟ajax的请求返回,按顺序进行,并传递每次返回的数据:

var urls = ['url1', 'url2', 'url3'];
function* request(urls) {
    var data;
    for (var i = 0, j = urls.length; i < j; ++i) {
        data = yield req(urls[i], data);
    }
}
var r = request(urls);
r.next();
function log(url, data, cb) {
    setTimeout(function() {
        cb(url);
    }, 1000);
}
function req(url, data) {
    var p = new Promise(function(resolve, reject) {
        log(url, data, function(rs) {
            if (!rs) {
                reject();
            } else {
                resolve(rs);
            }
        });
    });
    p.then(function(data) {
        console.log(data);
        r.next(data);
    }).catch(function() {
    });
}

达到了按顺序请求三个地址的效果,初始直接r.next()无参数,后续通过r.next(data)将data数据传入。


20200801150220845.png


注意代码的第16行,这里参数用了url变量,是为了和data数据做对比。


因为初始next()没有参数,若是直接将url换成data的话,就会因为promise对象的数据判断 !rs == undefined 而reject。


所以将第16行换成 cb(data || url)。


通过模拟的ajax输出,可了解到next的传参值,第一次在log输出的是 url = 'url1'值,后续将data = 'url1'传入req请求,在log中输出 data = 'url1'值。


for...of循环代替.next()

除了使用.next()方法遍历迭代器对象外,通过ES6提供的新循环方式for…of也可遍历,但与next不同的是,它会忽略return返回的值,如:

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}
var show = showNumbers();
for (var n of show) {
    console.log(n) // 1 2
}

此外,处理for…of循环,具有调用迭代器接口的方法方式也可遍历生成器函数,如扩展运算符…的使用:

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}
var show = showNumbers();
[...show] // [1, 2, length: 2]

更多使用可以参考:MDN - Generator


相关文章
|
2月前
|
前端开发 JavaScript
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
376 8
|
2月前
|
设计模式 前端开发 JavaScript
前端编程的异步解决方案有哪些?
本文首发于微信公众号“前端徐徐”,介绍了异步编程的背景和几种常见方案,包括回调、事件监听、发布订阅、Promise、Generator、async/await和响应式编程。每种方案都有详细的例子和优缺点分析,帮助开发者根据具体需求选择最合适的异步编程方式。
85 1
|
7月前
|
缓存 监控 前端开发
前端性能优化以及解决方案
前端性能优化关乎用户体验和网站竞争力,包括减少HTTP请求、使用CDN、压缩资源、延迟加载、利用浏览器缓存等策略。制定优化计划,使用监控工具,遵循最佳实践并持续学习,能提升网站速度和稳定性。
87 0
|
4月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
38 0
|
4月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
64 0
|
4月前
|
前端开发 测试技术 API
现代前端开发中的跨平台挑战与解决方案探讨
随着移动设备和桌面端用户体验的日益融合,现代前端开发面临着跨平台兼容性的重大挑战。本文将探讨这些挑战的根源,并介绍一些创新的解决方案,帮助开发人员更好地应对不同平台之间的差异,提升应用程序的用户体验和性能。
|
6月前
|
开发框架 前端开发 JavaScript
现代前端开发中的跨平台解决方案比较与应用
在现代软件开发中,跨平台解决方案成为了开发者们的热门话题。本文将探讨几种主流的跨平台开发框架及其特点,重点比较它们在前端开发中的应用场景和优劣势。通过对比分析,读者可以更好地理解如何选择适合自己项目需求的跨平台解决方案。
|
6月前
|
前端开发
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
|
5月前
|
前端开发
纯前端模拟后端接口异步获取数据
纯前端模拟后端接口异步获取数据
43 0
|
7月前
|
前端开发
如何处理前端应用程序中的异步操作
前端异步操作常见方法包括回调函数、Promise 和 async/await。回调函数可能导致回调地狱,Promise 提供了更好的错误处理和链式调用,而 async/await 则基于 Promise,以同步风格处理异步任务,提高代码可读性。根据需求和喜好选择相应方法,以实现更可靠、易维护的代码。

热门文章

最新文章