当前位置:K88软件开发文章中心编程语言JavaScriptECMAScript → 文章内容

ECMAScript 6 异步操作和Async函数

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 15:38:39

体内的变量y接收。因此,这一步的 value 属性,返回的就是2(变量y的值)。Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y;}var g = gen(1);g.next();g.throw('出错了');// 出错了上面代码的最后一行,Generator函数体外,使用指针对象的throw方法抛出的错误,可以被函数体内的try ...catch代码块捕获。这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。异步任务的封装下面看看如何使用 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);}上面代码中,Generator函数封装了一个异步操作,该操作先读取一个远程接口,然后从JSON格式的数据解析信息。就像前面说过的,这段代码非常像同步操作,除了加上了yield命令。执行这段代码的方法如下。var g = gen();var result = g.next();result.value.then(function(data){ return data.json();}).then(function(data){ g.next(data);});上面代码中,首先执行Generator函数,获取遍历器对象,然后使用next 方法(第二行),执行异步任务的第一阶段。由于Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next 方法。可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。Thunk函数参数的求值策略Thunk函数早在上个世纪60年代就诞生了。那时,编程语言刚刚起步,计算机学家还在研究,编译器怎么写比较好。一个争论的焦点是"求值策略",即函数的参数到底应该何时求值。var x = 1;function f(m){ return m * 2;}f(x + 5)上面代码先定义函数f,然后向它传入表达式x + 5。请问,这个表达式应该何时求值?一种意见是"传值调用"(call by value),即在进入函数体之前,就计算x + 5的值(等于6),再将这个值传入函数f 。C语言就采用这种策略。f(x + 5)// 传值调用时,等同于f(6)另一种意见是"传名调用"(call by name),即直接将表达式x + 5传入函数体,只在用到它的时候求值。Haskell语言采用这种策略。f(x + 5)// 传名调用时,等同于(x + 5) * 2传值调用和传名调用,哪一种比较好?回答是各有利弊。传值调用比较简单,但是对参数求值的时候,实际上还没用到这个参数,有可能造成性能损失。function f(a, b){ return b;}f(3 * x * x - 2 * x - 1, x);上面代码中,函数f的第一个参数是一个复杂的表达式,但是函数体内根本没用到。对这个参数求值,实际上是不必要的。因此,有一些计算机学家倾向于"传名调用",即只在执行时求值。Thunk函数的含义编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做Thunk函数。function f(m){ return m * 2;}f(x + 5);// 等同于var thunk = function () { return x + 5;};function f(thunk){ return thunk() * 2;}上面代码中,函数f的参数x + 5被一个函数替换了。凡是用到原参数的地方,对Thunk函数求值即可。这就是Thunk函数的定义,它是"传名调用"的一种实现策略,用来替换某个表达式。JavaScript语言的Thunk函数JavaScript语言是传值调用,它的Thunk函数含义有所不同。在JavaScript语言中,Thunk函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。// 正常版本的readFile(多参数版本)fs.readFile(fileName, callback);// Thunk版本的readFile(单参数版本)var readFileThunk = Thunk(fileName);readFileThunk(callback);var Thunk = function (fileName){ return function (callback){ return fs.readFile(fileName, callback); };};上面代码中,fs模块的readFile方法是一个多参数函数,两个参数分别为文件名和回调函数。经过转换器处理,它变成了一个单参数函数,只接受回调函数作为参数。这个单参数版本,就叫做Thunk函数。任何函数,只要参数有回调函数,就能写成Thunk函数的形式。下面是一个简单的Thunk函数转换器。// ES5版本var Thunk = function(fn){ return function (){ var args = Array.prototype.slice.call(arguments); return function (callback){ args.push(callback); return fn.apply(this, args); } };};// ES6版本var Thunk = function(fn) { return function (...args) { return function (callback) { return fn.call(this, ...args, callback); } };};使用上面的转换器,生成fs.readFile的Thunk函数。var readFileThunk = Thunk(fs.readFile);readFileThunk(fileA)(callback);下面是另一个完整的例子。function f(a, cb) { cb(a);}let ft = Thunk(f);let log = console.log.bind(console);ft(1)(log) // 1Thunkify模块生产环境的转换器,建议使用Thunkify模块。首先是安装。$ npm install thunkify使用方式如下。var thunkify = require('thunkify');var fs = require('fs');var read = thunkify(fs.readFile);read('package.json')(function(err, str){ // ...});Thunkify的源码与上一节那个简单的转换器非常像。function thunkify(fn){ return function(){ var args = new Array(arguments.length); var ctx = this; for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } return function(done){ var called; args.push(function(){ if (called) return; called = true; done.apply(null, arguments); }); try { fn.apply(ctx, args); } catch (err) { done(err); } } }};它的源码主要多了一个检查机制,变量called确保回调函数只运行一次。这样的设计与下文的Generator函数相关。请看下面的例子。function f(a, b, callback){ var sum = a + b; callback(sum); callback(sum);}var ft = thunkify(f);var print = co

上一页  [1] [2] [3] [4] [5] [6]  下一页


ECMAScript 6 异步操作和Async函数