-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ES6 系列之 Generator 的自动执行 #99
Comments
写的非常好 |
非常喜欢冴羽写的这几个系列,对于js基础薄弱的同学有很大帮助,在框架横行的年代,js一些基本概念和特性很多人都不是很扎实。看看挺好,哈哈 |
这章主要是思想,虽然有的地方有点跳,但是作者写的棒棒哒 |
看完《深入理解ES6》的这几章,再回过头来看这篇文章,非常Nice!!! |
写的很好! var g = gen(); 的 var i = gen(); 生成器函数执行以后返回的是迭代器,这样语义化更好一点😂 |
作为一个大学生,真的想不到哪个业务会用到yield+回调函数(哪位大佬可以给我科普一下。。。)。yield后面跟一个回调函数好像没有什么意义,因为回调函数无非是xhr or 模拟的异步代码 or 同步代码。同步代码我倒是用到过(判断相似树)。但是前2种情况真的想不到。。。 |
umijs……请查看 model 中 effects 对象中的写法 |
这个co 是不是就是后来的async/await? |
好像和阮一峰写的es6教程基本一样诶 |
可以这么理解的。Async就是Generator的语法糖。 |
之前去在看 阮一峰 的 es6 generator 异步操作的时候发现 老师并没有详细的写isPromise 和 toPromise 还好 经过多个文章的参考链接跳转到您这里 文章写的非常不错, 很喜欢你的写作风格 |
单个异步任务
为了获得最终的执行结果,你需要这样做:
首先执行 Generator 函数,获取遍历器对象。
然后使用 next 方法,执行异步任务的第一阶段,即 fetch(url)。
注意,由于 fetch(url) 会返回一个 Promise 对象,所以 result 的值为:
最后我们为这个 Promise 对象添加一个 then 方法,先将其返回的数据格式化(
data.json()
),再调用 g.next,将获得的数据传进去,由此可以执行异步任务的第二阶段,代码执行完毕。多个异步任务
上节我们只调用了一个接口,那如果我们调用了多个接口,使用了多个 yield,我们岂不是要在 then 函数中不断的嵌套下去……
所以我们来看看执行多个异步任务的情况:
为了获得最终的执行结果,你可能要写成:
但我知道你肯定不想写成这样……
其实,利用递归,我们可以这样写:
其中的关键就是 yield 的时候返回一个 Promise 对象,给这个 Promise 对象添加 then 方法,当异步操作成功时执行 then 中的 onFullfilled 函数,onFullfilled 函数中又去执行 g.next,从而让 Generator 继续执行,然后再返回一个 Promise,再在成功时执行 g.next,然后再返回……
启动器函数
在 run 这个启动器函数中,我们在 then 函数中将数据格式化
data.json()
,但在更广泛的情况下,比如 yield 直接跟一个 Promise,而非一个 fetch 函数返回的 Promise,因为没有 json 方法,代码就会报错。所以为了更具备通用性,连同这个例子和启动器,我们修改为:只要 yield 后跟着一个 Promise 对象,我们就可以利用这个 run 函数将 Generator 函数自动执行。
回调函数
yield 后一定要跟着一个 Promise 对象才能保证 Generator 的自动执行吗?如果只是一个回调函数呢?我们来看个例子:
首先我们来模拟一个普通的异步请求:
我们将这种函数改造成:
对于这样的 Generator 函数:
如果要获得最终的结果:
如果写成这样的话,我们会面临跟第一节同样的问题,那就是当使用多个 yield 时,代码会循环嵌套起来……
同样利用递归,所以我们可以将其改造为:
run
由此可以看到 Generator 函数的自动执行需要一种机制,即当异步操作有了结果,能够自动交回执行权。
而两种方法可以做到这一点。
(1)回调函数。将异步操作进行包装,暴露出回调函数,在回调函数里面交回执行权。
(2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。
在两种方法中,我们各写了一个 run 启动器函数,那我们能不能将这两种方式结合在一些,写一个通用的 run 函数呢?我们尝试一下:
其实实现的很简单,判断 result.value 是否是 Promise,是就添加 then 函数,不是就直接执行。
return Promise
我们已经写了一个不错的启动器函数,支持 yield 后跟回调函数或者 Promise 对象。
现在有一个问题需要思考,就是我们如何获得 Generator 函数的返回值呢?又如果 Generator 函数中出现了错误,就比如 fetch 了一个不存在的接口,这个错误该如何捕获呢?
这很容易让人想到 Promise,如果这个启动器函数返回一个 Promise,我们就可以给这个 Promise 对象添加 then 函数,当所有的异步操作执行成功后,我们执行 onFullfilled 函数,如果有任何失败,就执行 onRejected 函数。
我们写一版:
与第一版有很大的不同:
首先,我们返回了一个 Promise,当
result.done
为 true 的时候,我们将该值resolve(result.value)
,如果执行的过程中出现错误,被 catch 住,我们会将原因reject(e)
。其次,我们会使用
thunkToPromise
将回调函数包装成一个 Promise,然后统一的添加 then 函数。在这里值得注意的是,在thunkToPromise
函数中,我们遵循了 error first 的原则,这意味着当我们处理回调函数的情况时:在成功时,第一个参数应该返回 null,表示没有错误原因。
优化
我们在第二版的基础上将代码写的更加简洁优雅一点,最终的代码如下:
co
如果我们再将这个启动器函数写的完善一些,我们就相当于写了一个 co,实际上,上面的代码确实是来自于 co……
而 co 是什么? co 是大神 TJ Holowaychuk 于 2013 年 6 月发布的一个小模块,用于 Generator 函数的自动执行。
如果直接使用 co 模块,这两种不同的例子可以简写为:
是不是特别的好用?
ES6 系列
ES6 系列目录地址:https://github.com/mqyqingfeng/Blog
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: