从C#到TypeScript - Promise
总目录
从C#到TypeScript - Promise
背景
相信之前用过JavaScript的朋友都碰到过异步回调地狱(callback hell),N多个回调的嵌套不仅让代码读起来十分困难,维护起来也很不方便。
其实C#在Task
出现之前也是有类似场景的,Async Programming Mode时代,用Action
和Func
做回调也很流行,不过也是意识到太多的回调嵌套代码可读性差且维护不易,微软引入了Task
和Task-based Async Pattern。
虽然不知道是哪个语言最早有这个概念,但相信是C#把async await
带到流行语言的舞台,接着其他语言也以不同的形式支持async await
,如Python, Dart, Swift等。
JavaScript同样在ES6开始支持Promise
和Generator
,并在ES7中提出支持async await
的议案。
这篇先来看看Promise:
Promise的特点
Promise
之于TypeScript,相当于Task
之于C#,只有返回Promise
的函数才能使用async await
。
Promise
其实就是一个可以获取异步结果,并封装了一些异步操作的对象。
有三个状态:
pending
: 进行中
resolved
: 成功
rejected
: 失败
并且这三个状态只有两种转换:pending
->resolved
、pending
->rejected
,不是成功就是失败,并没有多余的状态转换。
这两种转换都是由异步返回的结果给定的,成功取回数据就是resolved
,取数据出异常就是rejected
。
也因此,这转换过后的结果就是固定的了,不可能在转换过后还会变回pending
或其他状态。
Promise
不能在任务进行中取消,只能等结果返回,这点上不如C#的Task
,Task
可以通过CancelTaskToken
来取消任务。
Promise的使用
可以直接new一个Promise
对象,构造函数的参数是一个有两个参数的函数。
这两个参数一个是resove
,用来在异步操作成功后调用,并把异步结果传出去,调用resove
后状态就由pending
->resolved
。
另一个是reject
,用来在失败或异常时调用,并把错误消息传出去,调用reject
后状态由pending
->rejected
。
var promise = new Promise(function(resolve, reject) {
});
通常需要在成功或失败后做一些操作,这时需要then
来做这个事,then
可以有两个函数参数,第一个是成功后调用的,第二个是失败调用的,第二个是可选的。
另外,then
返回的也是一个Promise,不过不是原来的那个,而是新new出来的,这样可以链式调用,then
后面再接then
。
// 函数参数用lambda表达式写更简洁
promise.then(success => {
console.info(success);
}, error => {
console.info(error);
}).then(()=>console.info('finish'));
嵌套的Promise
在实际场景中,我们可能需要在一个异步操作后再接个异步操作,这样就会有Promise
的嵌套操作。
下面的代码显示的是Promise
的嵌套操作:
p1
先打印"start",延时两秒打印"p1"。
p2
在p1
完成后延时两秒打印"p2"。
function delay(): Promise<void>{
return new Promise<void>((resolve, reject)=>{setTimeout(()=>resolve(), 2000)});
}
let p1 = new Promise((resolve, reject) => {
console.info('start');
delay().then(()=>{
console.info('p1');
resolve()
});
});
let p2 = new Promise((resolve, reject) => {
p1.then(()=>delay().then(()=>resolve()));
});
p2.then(()=>console.info('p2'));
异常处理
上面提到Promise
出错时把状态变为rejected
并把错误消息传给reject
函数,在then
里面调用reject
函数就可以显示异常。
不过这样写显得不是很友好,Promise
还有个catch
函数专门用来处理错误异常。
而且Promise
的异常是冒泡传递的,最后面写一个catch
就可以捕获到前面所有promise可能发生的异常,如果用reject
就需要每个都写。
所以reject
函数一般就不需要在then
里面写,在后面跟个catch
就可以了。
new Promise(function(resolve, reject) {
throw new Error('error');
}).catch(function(error) {
console.info(error); // Error: error
});
也如上面所说状态只有两种变化且一旦变化就固定下来,所以如果已经在Promise
里执行了resolve
,再throw异常是没用的,catch不到,因为状态已经变成resolved
。
new Promise(function(resolve, reject) {
resolve('success');
throw new Error('error');
}).catch(function(error) {
console.info(error); // 不会执行到这里
});
另外,catch
里的代码也可能出异常,所以catch
后面也还可以跟catch
的议案。
new Promise(function(resolve, reject) {
throw new Error('error');
}).catch(function(error) {
console.info(error); // Error: error
throw new Error('catch error');
}).catch(function(error){
console.info(error); // Error: catch error
};
BlueBird的 finally 和 done
异常的try...catch
后面可以跟finally
来执行必须要执行的代码,Promise
原生并不支持,可以引入BlueBird的扩展库来支持。
另外还有done
在最后面来表示执行结束并抛出可能出现的异常,比如最后一个catch
代码块里的异常。
let p = new Promise(function(resolve, reject) {
x = 2; // error, 没有声明x变量
resolve('success');
}).catch(function(error) {
console.info(error);
}).finally(()=>{ // 总会执行这里
console.info('finish');
y = 2; // error, 没有声明y变量
}).done();
try{
p.then(()=>console.info('done'));
} catch (e){
console.info(e); // 由于最后面的done,所以会把finally里的异常抛出来,如果没有done则不会执行到这里
}
并行执行Promise
虽然JavaScript是单线程语言,但并不妨碍它执行一些IO并行操作,如不阻塞发出http request,然后异步等待。
Promise
除了用then
来顺序执行外,也同样可以不阻塞同时执行多个Promise
然后等所有结果返回再进行后续操作。
C#的Task
有个WhenAll
的静态方法来做这个事,Promise
则是用all
方法达到同样目的。
all
方法接受实现Iterator接口的对象,比如数组。
let p = Promise.all([p1, p2, p3]);
all
返回的是一个新的Promise
- p,p的状态是由p1, p2, p3同时决定的:
p.resolved = p1.resolve && p2.resolve && p3.resolve
p.rejected = p1.rejected || p2.rejected || p3.rejected
也就是说p的成功需要p1,p2,p3都成功,而只要p1, p2, p3里有任何一个失败则p失败并退出。
Promise
还有一个方法race
同样是并行执行多个Promise
,不同于all
的是它的成功状态和错误状态一样,只要有一个成功就成功,如同C# Task的Any
方法。
let p = Promise.race([p1, p2, p3]);
从C#到TypeScript - Promise的更多相关文章
- [Typescript] Promise based delay function using async / await
Learn how to write a promise based delay function and then use it in async await to see how much it ...
- C# vs TypeScript - 高级类型
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 变量
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 接口
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 类
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - function
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 装饰器
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - Generator
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - async await
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
随机推荐
- tp框架里的 删改
//显示主页面 <table width="> <tr> <td>代号</td> <td>名称</td> <td ...
- ARM的启动代码(1):介绍(转)
源:ARM的启动代码(1):介绍 很多朋友搞嵌入式,写起代码来一点问题没有,到最后上板子调试的时候,挂了.究其原因,还是对芯片的启动地址.启动方式.bootloader和操作系统的衔接出了问题.今天就 ...
- OO设计原则 -- OO设计的原则及设计过程的全面总结
这部分增加一点自己的感想,OO设计原则下面讲述的很清晰;看完之后有点感想如果我们在实际开发当中能够把这些原则熟烂于心的话那我们的代码质量和个人能力会有很显著的提神.根据自己的实际经验看很多开发者在开发 ...
- 试水mongodb er
1)data ready var a = {"name":"zhekou","CharDate":"2015-12-01" ...
- Windows录音API学习笔记(转)
源:Windows录音API学习笔记 Windows录音API学习笔记 结构体和函数信息 结构体 WAVEINCAPS 该结构描述了一个波形音频输入设备的能力. typedef struct { W ...
- php 实例说明 socket通信机制
php 实例说明 socket通信机制 张映 发表于 2010-04-24 分类目录: php 一,socket是什么 什么是socket 所谓socket通常也称作"套接字",用 ...
- cordova环境搭建
首先,需要了解一下cordova 和 phonegap 的关系.phoneGap是原先的名字,Cordova是phoneGap被捐给apache之后 用的项目名,phoneGap的名字也被保留了. 1 ...
- 安装pybloomfiltermmap 遇到bug
pybloomfiltermmap pip 安装 : sudo pip install pybloomfiltermmap I want to try one program which have m ...
- 一个参数引起的mysql从库宕机血案
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://suifu.blog.51cto.com/9167728/1859252 一个参数 ...
- 2.5. Integer 16 、Integer 32、Integer 64(Core Data 应用程序实践指南)
Core Data 使用 “带符号的整数”,通常我们会选择Integer 32,如果不够,可以升级为Integer 64 (第3章),通过升级托管对象模型. 这三种类型对应的特性(Property)类 ...