摘要:前言在异步编程之一中实现了一个异步函数调用链,它是一个顺序调用链,很类似责任链模式,但现实往往不是平铺直叙的,更多的其实是峰回路转,本文将继续讨论更多的用法。
前言
在《ES6 异步编程之一:Generator》中实现了一个异步函数调用链,它是一个顺序调用链,很类似责任链模式,但现实往往不是平铺直叙的,更多的其实是峰回路转,本文将继续讨论更多Generator的用法。
作为函数的Generator在之前的示例中,我们更多的是把生成器作为一个迭代器来循环调用每个函数,但忽视了生成器也是个函数,意味着生成器内部可以实现复杂的业务逻辑,以下的代码通过yield等待文件读取结果并对结果进行特定的处理,
function* generator() { let r1 = yield get("a"); if (r1) { let r2 = yield get("b"); if (r2) { console.log(yield get("d")); } } else { console.log(yield get("c")); } } let g = generator(); g.next();
如果get是个异步调用,以上的代码想要能够执行则需要get函数执行得到结果后调用生成器的next方法来推进,这要求get函数能持有生成器对象,这显然并不容易。
偏函数偏函数(Partial Function)是对函数定义域的子集定义的函数,形式上就是指定任意部分参数生成一个新的函数,如下:
function sum(a, b, c) { return a + b + c; } function sum1(a) { return function(b, c) { return a + b + c; }; } function sum2(a, b) { return function(c) { return a + b + c; }; } sum(1, 2, 3) == sum1(1)(2, 3); //true sum(1, 2, 3) == sum2(1, 2)(3); //true
一般在设计异步调用api时,我们总是声明一个参数来接收回调函数,当和偏函数相结合就变成这样:
function get(f, callback) { delay(100, function(s) { callback(s + ":get " + f); }); } get("a", func); //调用get时必须立即传入一个函数 //转换成偏函数形式: function partialGet(f) { return function(callback) { delay(100, function(s) { callback(s + ":get " + f); }); }; } let pGet = partialGet("a"); //可以先生成一个函数 pGet(func); //需要时再传入回调函数执行
从上面的例子中可以发现,偏函数能使定义和执行分离,说来巧了,生成器可用于定义业务逻辑而生成器的next用于推进业务执行,二者也是相互分离的。
生成器和偏函数基于前面这么多铺垫,假设get就是一个偏函数,如下:
function get(f) { return function(callback) { delay(100, function(s) { callback(s + ":get " + f); }); }; }
这意味着,yield get("a")使得next函数执行的结果其value属性值是个函数,该函数的参数是一个能接收get异步结果的回调函数,即:
g.next().value(function(value) { g.next(value); //value成为yield的返回并继续推进业务逻辑 });
通过递归可以不断的执行生成器的next方法,一个全新的通过生成器来实现业务逻辑的run方法便呼之欲出了,
function run(gen) { let g = gen(); function next(lastValue) { let result = g.next(lastValue); //将上一个异步执行结果传出给当前的yield并执行下一个yield if (result.done) { return result.value; } //value是偏函数返回的新函数,它的参数是个用来接收异步结果的回调函数 result.value(next); //next作为接收异步结果的回调函数 } next(); } run(generator);
综合以上例子不难发现另外一个好处,通过偏函数可以使异步调用api不受生成器的侵入,《ES6 异步编程之一:Generator》中实现的异步调用需要将生成器作为参数,有兴趣的话你可以尝试改造一下之前的示例。
现在通过新编写的run函数就可以来执行本文一开始编写的那个生成器了。
thunkify偏函数的方案对api和生成器也是有侵入的,他要求:
api必须是偏函数形式;
生成器定义业务逻辑,每个yield 后面的函数必须是调用偏函数;
第二个问题是本方案的核心机制所要求,但第一个问题我们可以通过thunkify来解决,api依旧按照get(value, callback)方式定义,但定义生成器时需要将异步api调用通过thunkify转换为偏函数,如下:
let Thunkify = require("thunkify"); let thunkifiedGet = Thunkify(get); function get(f, callback) { delay(100, function(s) { callback(s + ":get " + f); }); } function* generator() { let r1 = yield thunkifiedGet("a"); if (r1) { let r2 = yield thunkifiedGet("b"); if (r2) { console.log(yield thunkifiedGet("d")); } } else { console.log(yield thunkifiedGet("c")); } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/81484.html
摘要:回调函数这是异步编程最基本的方法。对象对象是工作组提出的一种规范,目的是为异步编程提供统一接口。诞生后,出现了函数,它将异步编程带入了一个全新的阶段。 更多详情点击http://blog.zhangbing.club/Ja... Javascript 语言的执行环境是单线程的,如果没有异步编程,根本没法用,非卡死不可。 为了解决这个问题,Javascript语言将任务的执行模式分成两种...
摘要:传统的异步方法回调函数事件监听发布订阅之前写过一篇关于的文章,里边写过关于异步的一些概念。内部函数就是的回调函数,函数首先把函数的指针指向函数的下一步方法,如果没有,就把函数传给函数属性,否则直接退出。 Generator函数与异步编程 因为js是单线程语言,所以需要异步编程的存在,要不效率太低会卡死。 传统的异步方法 回调函数 事件监听 发布/订阅 Promise 之前写过一篇关...
摘要:环境中产生异步操作的函数分为两大类计时函数和函数。如果要在应用中定义复杂的异步操作,就要使用者两类异步函数作为基本的构造快。在本例子中同步事件循环不包含内部的外部的异步事件循环内部的。其弊端是操作强耦合维护代价高。 JavaScript环境中产生异步操作的函数分为两大类:计时函数和I/O函数。如果要在应用中定义复杂的异步操作,就要使用者两类异步函数作为基本的构造快。本文没有对某个知识点...
摘要:生成器是原生提供的异步编程方案,其语法行为和传统函数完全不同,阮大的入门一书中对生成器有比较详尽的介绍,还有一些其他的文章可以参考,比如入门深入浅出三生成器深入浅出十一生成器,续篇本文主要是通过一些代码示例来记录和总结生成器的用法。 Generator 生成器是es6原生提供的异步编程方案,其语法行为和传统函数完全不同,阮大的《ECMAScript 6 入门》一书中对生成器有比较详尽的...
摘要:更好的异步编程上面的方法可以适用于那些比较简单的异步工作流程。小结的组合目前是最强大,也是最优雅的异步流程管理编程方式。 访问原文地址 generators主要作用就是提供了一种,单线程的,很像同步方法的编程风格,方便你把异步实现的那些细节藏在别处。这让我们可以用一种很自然的方式书写我们代码中的流程和状态逻辑,不再需要去遵循那些奇怪的异步编程风格。 换句话说,通过将我们generato...
阅读 775·2021-11-24 09:38
阅读 941·2021-11-11 11:01
阅读 3148·2021-10-19 13:22
阅读 1490·2021-09-22 15:23
阅读 2806·2021-09-08 09:35
阅读 2745·2019-08-29 11:31
阅读 2102·2019-08-26 11:47
阅读 1543·2019-08-26 11:44