5、this调用语句必须是构造函数中的第一个可执行语句_ES6中的Promise和Generator详解...

40eceee1627253155f7d22128c8f7a19.png

简介

ES6中除了上篇文章讲过的语法新特性和一些新的API之外,还有两个非常重要的新特性就是Promise和Generator,今天我们将会详细讲解一下这两个新特性。

Promise

什么是Promise

Promise 是异步编程的一种解决方案,比传统的解决方案“回调函数和事件”更合理和更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise的特点

Promise有两个特点:

  1. 对象的状态不受外界影响。

Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

  1. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。

这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise的优点

Promise将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise的缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Promise的用法

Promise对象是一个构造函数,用来生成Promise实例:

var promise = new Promise(function(resolve, reject) { 
// ... some code 
if (/* 异步操作成功 */){ 
resolve(value); 
} else { reject(error); } 
}
);

promise可以接then操作,then操作可以接两个function参数,第一个function的参数就是构建Promise的时候resolve的value,第二个function的参数就是构建Promise的reject的error。

promise.then(function(value) { 
// success 
}, function(error) { 
// failure }
);

我们看一个具体的例子:

function timeout(ms){return new Promise(((resolve, reject) => {setTimeout(resolve,ms,'done');}))
}timeout(100).then(value => console.log(value));

Promise中调用了一个setTimeout方法,并会定时触发resolve方法,并传入参数done。

最后程序输出done。

Promise的执行顺序

Promise一经创建就会立马执行。但是Promise.then中的方法,则会等到一个调用周期过后再次调用,我们看下面的例子:

let promise = new Promise(((resolve, reject) => {console.log('Step1');resolve();
}));promise.then(() => {console.log('Step3');
});console.log('Step2');输出:
Step1
Step2
Step3

Promise.prototype.then()

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法.

getJSON("/users.json").then(function(json){return json.name;
}).then(function(name){console.log(name);
});

上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

getJSON("/users.json").then(function(json){return json.name;
}).catch(function(error){console.log(error);
});

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

getJSON("/users.json").then(function(json){return json.name;
}).then(function(name){console.log(name);
}).catch(function(error){//处理前面所有产生的错误console.log(error);
});

Promise.all()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例

var p = Promise.all([p1,p2,p3]);
  1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race()

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例

var p = Promise.race([p1,p2,p3]);

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数.

Promise.resolve()

Promise.resolve()将现有对象转为Promise对象.

Promise.resolve('js');
//等价于
new Promise(resolve => resolve('js'));

那么什么样的对象能够转化成为Promise对象呢?

  1. 参数是一个Promise实例
  2. 参数是一个thenable对象
  3. 参数不是具有then方法的对象,或根本就不是对象
  4. 不带有任何参数

Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

var p = Promise.reject('error');
//等价于
var p = new Promise((resolve,reject) => reject('error'));

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致

done()

Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误

asyncFunc().then(f1).catch(f2).then(f3).done();

finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行.

server.listen(1000).then(function(){//do something
}.finally(server.stop);

Generator

什么是Generator

Generator 函数是 ES6 提供的一种异步编程解决方案

从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态

执行 Generator 函数会返回一个遍历器对象.

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态。

举个例子:

function * helloWorldGenerator(){yield 'hello';yield 'world';return 'ending';
}var gen = helloWorldGenerator();

输出结果:

console.log(gen.next());
console.log(gen.next());
console.log(gen.next());{ value: 'hello', done: false }
{ value: 'world', done: false }
{ value: 'ending', done: true }

yield

遍历器对象的next方法的运行逻辑如下:

(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。

(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

注意,yield句本身没有返回值,或者说总是返回undefined。

next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。

function * f() {for( let i =0; true; i++){let reset = yield i;if(reset){i = -1;}}
}let g = f();
console.log(g.next());
console.log(g.next());
console.log(g.next(true));

输出结果:

{ value: 0, done: false }
{ value: 1, done: false }
{ value: 0, done: false }

可以看到最后的一步,我们使用next传入的true替代了i的值,最后导致i= -1 + 1 = 0.

我们再看一个例子:

function * f2(x){var y = 2 * ( yield ( x + 1));var z = yield (y / 3);return (x + y + z);
}var r1= f2(5);
console.log(r1.next());
console.log(r1.next());
console.log(r1.next());var r2= f2(5);
console.log(r2.next());
console.log(r2.next(12));
console.log(r2.next(13));

输出结果:

{ value: 6, done: false }
{ value: NaN, done: false }
{ value: NaN, done: true }{ value: 6, done: false }
{ value: 8, done: false }
{ value: 42, done: true }

如果next不传值的话,yield本身是没有返回值的,所以我们会得到NaN。

但是如果next传入特定的值,则该值会替换该yield,成为真正的返回值。

yield *

如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的

function * a1(){yield 'a';yield 'b';
}function * b1(){yield 'x';a1();yield 'y';
}for(let v of b1()){console.log(v);
}

输出结果:

x
y

可以看到,在b1中调用a1是没有效果的。

将上面的例子修改一下:

function * a1(){yield 'a';yield 'b';
}function * b1(){yield 'x';yield * a1();yield 'y';
}for(let v of b1()){console.log(v);
}

输出结果:

x
a
b
y

异步操作的同步化表达

Generator函数的暂停执行的效果,意味着可以把异步操作写在yield语句里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield语句下面,反正要等到调用next方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。

我们看一个怎么通过Generator来获取一个Ajax的结果。

function * ajaxCall(){let result = yield request("http://www.flydean.com");let resp = JSON.parse(result);console.log(resp.value);
}function request(url){makeAjaxCall(url, function(response){it.next(response);});
}var it = ajaxCall();
it.next();

我们使用一个yield来获取异步执行的结果。但是我们如何将这个yield传给result变量呢?要记住yield本身是没有返回值的。

我们需要调用generator的next方法,将异步执行的结果传进去。这就是我们在request方法中做的事情。

Generator 的异步应用

什么是异步应用呢?

所谓”异步”,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。

相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。

ES6诞生以前,异步编程的方法,大概有下面四种。
回调函数
事件监听
发布/订阅
Promise 对象

回调函数

fs.readFile(fileA, 'utf-8', function(error,data){fs.readFile(fileB, 'utf-8', function(error,data){
}
})

如果依次读取两个以上的文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为”回调函数地狱”(callback hell)。

Promise

Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。

let readFile = require('fs-readfile-promise');
readFile(fileA).then(function(){return readFile(fileB);
}).then(function(data){console.log(data);
})

Thunk函数和异步函数自动执行

在讲Thunk函数之前,我们讲一下函数的调用有两种方式,一种是传值调用,一种是传名调用。

“传值调用”(call by value),即在进入函数体之前,就计算x + 5的值(等于6),再将这个值传入函数f。C语言就采用这种策略。

“传名调用”(call by name),即直接将表达式x + 5传入函数体,只在用到它的时候求值。

编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。

举个例子:

function f(m){return m * 2;
}f(x + 5);

上面的代码等于:

var thunk = function () {return x + 5;
}
function f(thunk){return thunk() * 2;
}

在 JavaScript 语言中,Thunk函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。

怎么解释呢?

比如nodejs中的:

fs.readFile(filename,[encoding],[callback(err,data)])

readFile接收3个参数,其中encoding是可选的。我们就以两个参数为例。

一般来说,我们这样调用:

fs.readFile(fileA,callback);

那么有没有办法将其改写成为单个参数的function的级联调用呢?

var Thunk = function (fn){return function (...args){return functon (callback){return fn.call(this,...args, callback);}}
}var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);

可以看到上面的Thunk将两个参数的函数改写成为了单个参数函数的级联方式。或者说Thunk是接收一个callback并执行方法的函数。

这样改写有什么用呢?Thunk函数现在可以用于 Generator 函数的自动流程管理。

之前在讲Generator的时候,如果Generator中有多个yield的异步方法,那么我们需要在next方法中传入这些异步方法的执行结果。

手动传入异步执行结果当然是可以的。但是有没有自动执行的办法呢?

let fs = require('fs');
let thunkify = require('thunkify');
let readFileThunk = thunkify(fs.readFile);let gen = function * (){let r1 = yield readFileThunk('/tmp/file1');console.log(r1.toString());let r2 = yield readFileThunk('/tmp/file2');console.log(r2.toString());
}let g = gen();function run(fn){let gen = fn();function next (err, data){let result = gen.next(data);if(result.done) return;result.value(next);}next();
}run(g);

gen.next返回的是一个对象,对象的value就是Thunk函数,我们向Thunk函数再次传入next callback,从而出发下一次的yield操作。

有了这个执行器,执行Generator函数方便多了。不管内部有多少个异步操作,直接把 Generator 函数传入run函数即可。当然,前提是每一个异步操作,都要是Thunk函数,也就是说,跟在yield命令后面的必须是Thunk函数。

总结

Promise和Generator是ES6中引入的非常中要的语法,后面的koa框架就是Generator的一种具体的实现。我们会在后面的文章中详细讲解koa的使用,敬请期待。

本文作者:flydean程序那些事本文链接:http://www.flydean.com/es6-promise-generator/本文来源:flydean的博客欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/505903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华中师范大学本科生计算机课教师,华中师范大学第八届大学生计算机设计大赛的通知...

为了激发大学生学习计算机知识和技能的兴趣和潜能,培养其创新能力和团队合作意识,以及运用信息技术解决实际问题的综合实践能力,切实提高计算机教学质量,教育部高等学校计算机类专业教学指导委员会、软件工程专业教学指导委员会、…

labelimg如何调整框的颜色_如何制作摄影集(下)

如果说上篇讲的是有关排版设计,那么下篇就是有关具体实施了。如果你正对作品集的制作跃跃欲试,那么下篇的内容仍旧非常重要,而且非常干货。 目录:软件推荐(Id)字体推荐纸张选择打印方式(激光、喷…

highcharts 显示平均值数值_拼多多评价多久能显示,有什么出评价技巧吗?

很多商家都知道,拼多多的评价,有时不一定会在买家评价后就立马出现,一般得等一段时间才会显示,,这个时间一般不会很久,24小时之内,评论一般在审核通过后就会展示出来。如果长时间不展示&#xf…

辽宁省大学生计算机系统与程序设计竞赛,2019CCF大学生计算机系统与程序设计竞赛(华东分赛区)在我校顺利举办...

5月18日,2019CCF大学生计算机系统与程序设计竞赛(CCSP)华东分赛区比赛及颁奖会在我校举办。今年是CCSP大赛首次采用区域分赛区的比赛,共分为7个赛区,包括东北区(哈尔滨工业大学承办),华北区(北京邮电大学承办)、华东区(南京航空航…

arm b bl 地址无关码_ARM汇编语言入门(六)

Part 6:条件状态和分支在探讨CPSR时我们已经接触了条件状态。我们通过跳转(分支)或者一些只有满足特定条件才执行的指令来控制程序在运行时的执行流。通过CPSR寄存器中的特定bit位来表示条件状态。这些位根据指令每次执行的结果而不断变化。例…

bcp out 带列名导出_从零开始学习 MySQL 系列索引、视图、导入和导出

阅读本文大概需要 8 分钟前言上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识。作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会了解&#x…

四年级计算机课程,信息技术(四年级)全部课程PPT课件.ppt

信息技术(四年级)全部课程PPT课件调整窗口的大小 最小化 最大化 关闭 水平的双向箭头可以调整窗口的宽度,让窗口变宽或变窄。 调整窗口的大小 最小化 最大化 关闭 倾斜的双向箭头可同时调整窗口的高和宽。 做一做 打开我的文档,对窗口进行最小化、最大化…

python等腰梯形_简单空实心图形打印|Python练习系列[3]

def shape_print(n): #实心等腰三角形 for y in range(n): for x in range(n-y-1):#先循环打印空格 形成一个倒直角三角形 range()中的值是形成的规律 print( ,end) for z in range(y*21):#再循环打印X 形成一个等腰三角形 range()中的值是形成的规律 print(X,end) print() pri…

adas记录仪app_路影行车记录仪app

路影行车记录仪app具有非常强大实用的行车视频记录功能,能够清晰的记录车主的行车过程,路影app可以通过手机对路影行车记录仪进行设置,预览、回放、下载图片/视频等操作,p还能保存各种视频文件,下载导航地图等。路影行…

连接硬盘计算机没显示,新买的移动硬盘在我的电脑中无法显示,但是右下角图标显示已经连接,? 爱问知识人...

USB不被电脑识别,如果是系统或系统自带的驱动的原因,一般经过重启就可恢复或开机按F8进入安全模式在退出,在进入正常模式(修复受损的注册表)。U盘插入电脑,电脑提示“无法识别的设备”故障诊断方法如下。第1步:如果U盘…

爬虫python软件准备_工具准备的差不多了,接下来就是python爬虫的封装了

python爬虫的方便大家都懂的。那么,既然常用,那么我们当然要封装啦。 那么我们可以先封装一个父类的爬虫 我自己的设计想法就是,首先,爬虫必须要有个字段来存储匹配的规则gainRule,然后有个字段存储需要取什么属性outA…

axure 输入框默认灰色字_Axure如何应对意外关闭

在使用Axure的时候,相信很多朋友都遇到过这样的情况:axure提交的东西都不见了。内容做到一半或者已经快要完成时,软件意外关闭,导致做的内容意外丢失。看着自己的劳动成果就这样浪费了,这种情况真的令人心疼。如果遇到…

计算机图像隐藏信息,学术讲座:杨庆隆-台湾成功大学-基于纠错码的信息隐藏与秘密图像共享...

学术讲座讲座题目:基于纠错码的信息隐藏与秘密图像共享讲座时间:2017.10.27。上午9:30-11:00讲座地点:十教10106讲座讲者:杨庆隆教授讲者简历:杨庆隆,台湾东华大学教授,博士&#xf…

matlaba绘制gps星空图_网络图横道图绘制软件 5.0免锁版告别纯手工绘制,修改工作量大!...

按图片加小编微信今日资料会员专属资料链接链接:https://pan.baidu.com/s/1AZY3cPeEv72GBRfESIwk_w提取码:88B8安装教程:1、下载压缩文件,解压后双击【网络计划V5.exe】,点击立即安装。2、没有替换补丁打开软件是这种情…

xutils找id空指针_xUtils更新到3.0后的基本使用规则

说实话,对于xUtils,是我最近才用到的开发框架(也是刚接触),对于其功能不得不说,简化了很多的开发步骤,可以说是非常好的开发工具,但是其最近更新到3.0也没有解决加载自定义ImageView报错的问题。我总是喜欢…

xp访问不了win10计算机,如何解决winxp访问win10共享打印机提示凭据不足

在win10的电脑上对着始按钮点鼠标右键,点击运行,或者直接“winR”输入gpedit.msc,点击确定,在本地策略组编辑器中依次点开——计算机配置——windows设置——安全设置——本地策略——安全选项,在右边的列表中找到“网…

centos7 关闭selinux_Devops之LDAP部署安装(centos7+openLDAP+PhpLDAPAdmin)

Devops之LDAP部署安装(centos7openLDAPPhpLDAPAdmin)由于公司部门的需求,需要搭建ldap来统一Devops的用户名和密码,具体的选择LDAP分析在上一篇里,这里主要记录一下部署centos7openLDAPPhpLDAPAdmin来实现Ldap服务,并使用phpldapa…

微信时代计算机教学,互联网+时代技工院校计算机教学方式研究

刘兆慧摘 要:互联网时代的到来让几乎每一个行业的生产经营方式发生巨大变革,教育行业也不可避免。本文就技工院校计算机在互联网时代背景下采取的教学方式进行研究。本次研究采取了实验法、观察法、比较法等研究方法。结果显示,在互联网环境下…

python编写add函数求和_为什么python不利用__iadd__来实现求和和链接运算符?

我刚做了一个有趣的测试:~$python3 # I also conducted this on python 2.7.6, with the same resultPython 3.4.0 (default, Apr 11 2014, 13:05:11)[GCC 4.8.2] on linuxType "help", "copyright", "credits" or "license&quo…

python count函数用法 comm_python3:MySQL 8.0学习笔记(第五部分:单表查询操作)

在讲解单表查询时,首先创建一个emp的员工表,表中字段包括:empno(员工编号)、ename(员工姓名)、job(员工职位)、mgr(员工领导)、hiredate&#xff…