返回顶部
首页 > 资讯 > 精选 >ES6中Generator自动执行怎么实现
  • 507
分享到

ES6中Generator自动执行怎么实现

2023-06-17 09:06:24 507人浏览 独家记忆
摘要

这篇文章主要讲解了“es6中Generator自动执行怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ES6中Generator自动执行怎么实现”吧!单个异步任务var fe

这篇文章主要讲解了“es6中Generator自动执行怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ES6中Generator自动执行怎么实现”吧!

单个异步任务

var fetch = require('node-fetch');function* gen(){    var url = 'https://api.GitHub.com/users/github';    var result = yield fetch(url);    console.log(result.bio);}

为了获得最终的执行结果,你需要这样做:

var g = gen();var result = g.next();result.value.then(function(data){    return data.JSON();}).then(function(data){    g.next(data);});

首先执行 Generator 函数,获取遍历器对象。

然后使用 next 方法,执行异步任务的第一阶段,即 fetch(url)。

注意,由于 fetch(url) 会返回一个 Promise 对象,所以 result 的值为:

{ value: Promise { <pending> }, done: false }

最后我们为这个 Promise 对象添加一个 then 方法,先将其返回的数据格式化(data.json()),再调用 g.next,将获得的数据传进去,由此可以执行异步任务的第二阶段,代码执行完毕。

多个异步任务

上节我们只调用了一个接口,那如果我们调用了多个接口,使用了多个 yield,我们岂不是要在 then 函数中不断的嵌套下去……

所以我们来看看执行多个异步任务的情况:

var fetch = require('node-fetch');function* gen() {    var r1 = yield fetch('Https://api.github.com/users/github');    var r2 = yield fetch('https://api.github.com/users/github/followers');    var r3 = yield fetch('https://api.github.com/users/github/repos');    console.log([r1.bio, r2[0].login, r3[0].full_name].join('\n'));}

为了获得最终的执行结果,你可能要写成:

var g = gen();var result1 = g.next();result1.value.then(function(data){    return data.json();}).then(function(data){    return g.next(data).value;}).then(function(data){    return data.json();}).then(function(data){    return g.next(data).value}).then(function(data){    return data.json();}).then(function(data){    g.next(data)});

但我知道你肯定不想写成这样……

其实,利用递归,我们可以这样写:

function run(gen) {    var g = gen();    function next(data) {        var result = g.next(data);        if (result.done) return;        result.value.then(function(data) {            return data.json();        }).then(function(data) {            next(data);        });    }    next();}run(gen);

其中的关键就是 yield 的时候返回一个 Promise 对象,给这个 Promise 对象添加 then 方法,当异步操作成功时执行 then 中的 onFullfilled 函数,onFullfilled 函数中又去执行 g.next,从而让 Generator 继续执行,然后再返回一个 Promise,再在成功时执行 g.next,然后再返回……

启动器函数

在 run 这个启动器函数中,我们在 then 函数中将数据格式化 data.json(),但在更广泛的情况下,比如 yield 直接跟一个 Promise,而非一个 fetch 函数返回的 Promise,因为没有 json 方法,代码就会报错。所以为了更具备通用性,连同这个例子和启动器,我们修改为:

var fetch = require('node-fetch');function* gen() {    var r1 = yield fetch('https://api.github.com/users/github');    var json1 = yield r1.json();    var r2 = yield fetch('https://api.github.com/users/github/followers');    var json2 = yield r2.json();    var r3 = yield fetch('https://api.github.com/users/github/repos');    var json3 = yield r3.json();    console.log([json1.bio, json2[0].login, json3[0].full_name].join('\n'));}function run(gen) {    var g = gen();    function next(data) {        var result = g.next(data);        if (result.done) return;        result.value.then(function(data) {            next(data);        });    }    next();}run(gen);

只要 yield 后跟着一个 Promise 对象,我们就可以利用这个 run 函数将 Generator 函数自动执行。

回调函数

yield 后一定要跟着一个 Promise 对象才能保证 Generator 的自动执行吗?如果只是一个回调函数呢?我们来看个例子:

首先我们来模拟一个普通的异步请求:

function fetchData(url, cb) {    setTimeout(function(){        cb({status: 200, data: url})    }, 1000)}

我们将这种函数改造成:

function fetchData(url) {    return function(cb){        setTimeout(function(){            cb({status: 200, data: url})        }, 1000)    }}

对于这样的 Generator 函数:

function* gen() {    var r1 = yield fetchData('https://api.github.com/users/github');    var r2 = yield fetchData('https://api.github.com/users/github/followers');    console.log([r1.data, r2.data].join('\n'));}

如果要获得最终的结果:

var g = gen();var r1 = g.next();r1.value(function(data) {    var r2 = g.next(data);    r2.value(function(data) {        g.next(data);    });});

如果写成这样的话,我们会面临跟第一节同样的问题,那就是当使用多个 yield 时,代码会循环嵌套起来……

同样利用递归,所以我们可以将其改造为:

function run(gen) {    var g = gen();    function next(data) {        var result = g.next(data);        if (result.done) return;        result.value(next);    }    next();}run(gen);

run

由此可以看到 Generator 函数的自动执行需要一种机制,即当异步操作有了结果,能够自动交回执行权。

而两种方法可以做到这一点。

(1)回调函数。将异步操作进行包装,暴露出回调函数,在回调函数里面交回执行权。

(2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

在两种方法中,我们各写了一个 run 启动器函数,那我们能不能将这两种方式结合在一些,写一个通用的 run 函数呢?我们尝试一下:

// 第一版function run(gen) {    var gen = gen();    function next(data) {        var result = gen.next(data);        if (result.done) return;        if (isPromise(result.value)) {            result.value.then(function(data) {                next(data);            });        } else {            result.value(next)        }    }    next()}function isPromise(obj) {    return 'function' == typeof obj.then;}module.exports = run;

其实实现的很简单,判断 result.value 是否是 Promise,是就添加 then 函数,不是就直接执行。

return Promise

我们已经写了一个不错的启动器函数,支持 yield 后跟回调函数或者 Promise 对象。

现在有一个问题需要思考,就是我们如何获得 Generator 函数的返回值呢?又如果 Generator 函数中出现了错误,就比如 fetch 了一个不存在的接口,这个错误该如何捕获呢?

这很容易让人想到 Promise,如果这个启动器函数返回一个 Promise,我们就可以给这个 Promise 对象添加 then 函数,当所有的异步操作执行成功后,我们执行 onFullfilled 函数,如果有任何失败,就执行 onRejected 函数。

我们写一版:

// 第二版function run(gen) {    var gen = gen();    return new Promise(function(resolve, reject) {        function next(data) {            try {                var result = gen.next(data);            } catch (e) {                return reject(e);            }            if (result.done) {                return resolve(result.value)            };            var value = toPromise(result.value);            value.then(function(data) {                next(data);            }, function(e) {                reject(e)            });        }        next()    })}function isPromise(obj) {    return 'function' == typeof obj.then;}function toPromise(obj) {    if (isPromise(obj)) return obj;    if ('function' == typeof obj) return thunkToPromise(obj);    return obj;}function thunkToPromise(fn) {    return new Promise(function(resolve, reject) {        fn(function(err, res) {            if (err) return reject(err);            resolve(res);        });    });}module.exports = run;

与第一版有很大的不同:

首先,我们返回了一个 Promise,当 result.done 为 true 的时候,我们将该值 resolve(result.value),如果执行的过程中出现错误,被 catch 住,我们会将原因 reject(e)。

其次,我们会使用 thunkToPromise 将回调函数包装成一个 Promise,然后统一的添加 then 函数。在这里值得注意的是,在 thunkToPromise 函数中,我们遵循了 error first 的原则,这意味着当我们处理回调函数的情况时:

// 模拟数据请求function fetchData(url) {    return function(cb) {        setTimeout(function() {            cb(null, { status: 200, data: url })        }, 1000)    }}

在成功时,第一个参数应该返回 null,表示没有错误原因。

优化

我们在第二版的基础上将代码写的更加简洁优雅一点,最终的代码如下:

// 第三版function run(gen) {    return new Promise(function(resolve, reject) {        if (typeof gen == 'function') gen = gen();        // 如果 gen 不是一个迭代器        if (!gen || typeof gen.next !== 'function') return resolve(gen)        onFulfilled();        function onFulfilled(res) {            var ret;            try {                ret = gen.next(res);            } catch (e) {                return reject(e);            }            next(ret);        }        function onRejected(err) {            var ret;            try {                ret = gen.throw(err);            } catch (e) {                return reject(e);            }            next(ret);        }        function next(ret) {            if (ret.done) return resolve(ret.value);            var value = toPromise(ret.value);            if (value && isPromise(value)) return value.then(onFulfilled, onRejected);            return onRejected(new TypeError('You may only yield a function, promise ' +                'but the following object was passed: "' + String(ret.value) + '"'));        }    })}function isPromise(obj) {    return 'function' == typeof obj.then;}function toPromise(obj) {    if (isPromise(obj)) return obj;    if ('function' == typeof obj) return thunkToPromise(obj);    return obj;}function thunkToPromise(fn) {    return new Promise(function(resolve, reject) {        fn(function(err, res) {            if (err) return reject(err);            resolve(res);        });    });}module.exports = run;

co

如果我们再将这个启动器函数写的完善一些,我们就相当于写了一个 co,实际上,上面的代码确实是来自于 co……

而 co 是什么? co 是大神 TJ Holowaychuk 于 2013 年 6 月发布的一个小模块,用于 Generator 函数的自动执行。

如果直接使用 co 模块,这两种不同的例子可以简写为:

// yield 后是一个 Promisevar fetch = require('node-fetch');var co = require('co');function* gen() {    var r1 = yield fetch('https://api.github.com/users/github');    var json1 = yield r1.json();    var r2 = yield fetch('https://api.github.com/users/github/followers');    var json2 = yield r2.json();    var r3 = yield fetch('https://api.github.com/users/github/repos');    var json3 = yield r3.json();    console.log([json1.bio, json2[0].login, json3[0].full_name].join('\n'));}co(gen);
// yield 后是一个回调函数var co = require('co');function fetchData(url) {    return function(cb) {        setTimeout(function() {            cb(null, { status: 200, data: url })        }, 1000)    }}function* gen() {    var r1 = yield fetchData('https://api.github.com/users/github');    var r2 = yield fetchData('https://api.github.com/users/github/followers');    console.log([r1.data, r2.data].join('\n'));}co(gen);

感谢各位的阅读,以上就是“ES6中Generator自动执行怎么实现”的内容了,经过本文的学习后,相信大家对ES6中Generator自动执行怎么实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: ES6中Generator自动执行怎么实现

本文链接: https://lsjlt.com/news/288073.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • ES6中Generator自动执行怎么实现
    这篇文章主要讲解了“ES6中Generator自动执行怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ES6中Generator自动执行怎么实现”吧!单个异步任务var fe...
    99+
    2023-06-17
  • es6中的generator怎么用
    本篇内容介绍了“es6中的generator怎么用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • SpringBoot怎么实现启动时自动执行代码
    这篇文章主要介绍了SpringBoot怎么实现启动时自动执行代码的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SpringBoot怎么实现启动时自动执行代码文章都会有所收获,下面我们一起来看看吧。前言目前开发的...
    99+
    2023-06-29
  • ES6中迭代器、Generator函数怎么用
    小编给大家分享一下ES6中迭代器、Generator函数怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、迭代器之前再聊迭...
    99+
    2024-04-02
  • CSS中怎么实现自动换行
    这篇文章将为大家详细讲解有关 CSS中怎么实现自动换行,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。CSS内容自动换行CSS自动换行问题你是否了解?正常字符...
    99+
    2024-04-02
  • php如何实现代码自动执行
    本文小编为大家详细介绍“php如何实现代码自动执行”,内容详细,步骤清晰,细节处理妥当,希望这篇“php如何实现代码自动执行”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、PHP的自动执行功能PHP提供了多种方...
    99+
    2023-07-06
  • Navicat中怎么自动化执行SQL脚本
    在Navicat中,可以使用任务计划(Scheduler)功能来自动化执行SQL脚本。以下是操作步骤: 打开Navicat软件,连...
    99+
    2024-05-10
    Navicat
  • golang动态执行代码怎么实现
    在Go语言中,可以使用reflect包来实现动态执行代码。下面是一个简单的示例: package main import ( ...
    99+
    2023-10-26
    golang
  • JavaScript中怎么使用generator函数同步执行ajax任务
    JavaScript中怎么使用generator函数同步执行ajax任务,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。具体内容如下functi...
    99+
    2024-04-02
  • Visual Studio 2010中自动执行属性怎么用
    这篇文章主要为大家展示了“Visual Studio 2010中自动执行属性怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Visual Studio 2010中自动执行属性怎么用”这篇文章...
    99+
    2023-06-17
  • 【shell】定期自动执行程序的shell实现
    【shell】定期自动执行程序的shell实现 1 chmod1.1 权限符号1.2 permission1.3 八进制语法1.4 操作者的身份介绍:所有者、用户组和其他人1.4.1 文件所有...
    99+
    2023-10-22
    linux 服务器 unix
  • Linux开机怎么自动执行脚本
    这篇“Linux开机怎么自动执行脚本”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Linux开机怎么自动执行脚本”文章吧。开...
    99+
    2023-06-28
  • 基于mybatis-plus-generator实现代码自动生成器
    目录1.引入依赖2.简单的代码生成3.自定义模板生成代码3.1实现思路3.2定义代码生成常量3.3全局配置3.4定义生成代码模板的路径3.5定义各文件生成存储路径3.6数据源配置3....
    99+
    2024-04-02
  • mysql中怎么实现定时执行
    mysql中怎么实现定时执行,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。5.1版本以后,MYSQL支持定时执行(event)的功能,就跟...
    99+
    2024-04-02
  • javascript中怎么自执行函数
    这期内容当中小编将会给大家带来有关javascript中怎么自执行函数,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。function (window, ...
    99+
    2024-04-02
  • PHPStorm如何实现自动执行代码格式化
    这篇文章主要介绍了PHPStorm如何实现自动执行代码格式化的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇PHPStorm如何实现自动执行代码格式化文章都会有所收获,下面我们一起来看看吧。在我们日常开发中,一定...
    99+
    2023-07-04
  • 怎么使用shell脚本执行命令实现自动填充密码
    这篇文章主要介绍“怎么使用shell脚本执行命令实现自动填充密码”,在日常操作中,相信很多人在怎么使用shell脚本执行命令实现自动填充密码问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用shell脚本...
    99+
    2023-07-05
  • Mybatis Generator自动生成对应文件的实现方法
     使用Generator自动生成我们需要的bean dao mapper xml等文件的过程(没有借助eclipse等编辑工具直接命令提示符生成)在E盘新建一个文件夹generator,在这文件夹下创建一个test文件夹用来存放生...
    99+
    2023-05-31
    mybatis generator rat
  • SpringBoot启动时自动执行代码的几种实现方式
    目录前言java自身的启动时加载方式static代码块构造方法Spring启动时加载方式代码测试总结前言 目前开发的SpringBoot项目在启动的时候需要预加载一些资源。而如何实现...
    99+
    2024-04-02
  • php怎么实现每天自动运行
    本文操作环境:linux5.9.8系统、PHP7.1版、Dell G3电脑。php做不到每天定时执行,只有java或者c可以,因为php是不访问页面就不会动得代码,但是有一个方法可以曲线救国你写一个页面是你要每天定时执行的页面,然后视你的操...
    99+
    2018-02-26
    php 自动运行
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作