总目录

从C#到TypeScript - Promise

背景

相信之前用过JavaScript的朋友都碰到过异步回调地狱(callback hell),N多个回调的嵌套不仅让代码读起来十分困难,维护起来也很不方便。

其实C#在Task出现之前也是有类似场景的,Async Programming Mode时代,用ActionFunc做回调也很流行,不过也是意识到太多的回调嵌套代码可读性差且维护不易,微软引入了Task和Task-based Async Pattern。

虽然不知道是哪个语言最早有这个概念,但相信是C#把async await带到流行语言的舞台,接着其他语言也以不同的形式支持async await,如Python, Dart, Swift等。

JavaScript同样在ES6开始支持PromiseGenerator,并在ES7中提出支持async await的议案。

这篇先来看看Promise:

Promise的特点

Promise之于TypeScript,相当于Task之于C#,只有返回Promise的函数才能使用async await

Promise其实就是一个可以获取异步结果,并封装了一些异步操作的对象。

有三个状态:

pending: 进行中

resolved: 成功

rejected: 失败

并且这三个状态只有两种转换:pending->resolvedpending->rejected,不是成功就是失败,并没有多余的状态转换。

这两种转换都是由异步返回的结果给定的,成功取回数据就是resolved,取数据出异常就是rejected

也因此,这转换过后的结果就是固定的了,不可能在转换过后还会变回pending或其他状态。

Promise不能在任务进行中取消,只能等结果返回,这点上不如C#的TaskTask可以通过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"。

p2p1完成后延时两秒打印"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的更多相关文章

  1. [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 ...

  2. C# vs TypeScript - 高级类型

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  3. 从C#到TypeScript - 变量

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  4. 从C#到TypeScript - 接口

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  5. 从C#到TypeScript - 类

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  6. 从C#到TypeScript - function

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  7. 从C#到TypeScript - 装饰器

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  8. 从C#到TypeScript - Generator

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  9. 从C#到TypeScript - async await

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

随机推荐

  1. Heka GeoIpDecoder 配置

    Prepare: 安装geoip-api-c,确保/usr/include/GeoIP.h存在: 源码编译安装Heka (容易出现问题): 下载GeoLiteCity.dat数据库. 配置文件举例: ...

  2. 如何用 Git 合并两个库,并保留提交历史

    转载自 https://segmentfault.com/a/1190000000678808 背景 一个中型规模项目,开始规划时就打算采用 C/S 架构,后端是单纯的 API 服务,前端在 Web ...

  3. 7.广播和多播,IGMP协议

    1.单播,多播,广播的介绍 1.1.单播(unicast) 单播是说,对特定的主机进行数据传送.例如给某一个主机发送IP数据包.这时候,数据链路层给出的数据头里面是非常具体的目的地址,对于以太网来 说 ...

  4. UIButton 之 按下高亮

    设置高亮 [btn setHighlighted:YES]; 设置按下时高亮 [btn setShowsTouchWhenHighlighted:YES];

  5. delphi的ArrayList

    本文转载自Top.hand<delphi的ArrayList>   delphi可以用Classes.TList类来实现ArrayList功能.注意:add()方法存入的类型是TPoint ...

  6. osgearth介绍(转载)-feature_labels.earth

    初识osg OSG的诞生 在 1997 年时,Don Burns 由于喜欢滑翔机运动且对计算机图形学非常熟悉,在 LINUX 上写了一个控制滑翔机的小引擎,这便是 OSG 的最初雏形.后来在 1998 ...

  7. eclipse修改主题配色

    1.Java-->Editor---> Syntax Coloring修改类中的各种代码颜色 2.General-->Editors --->Text Editors

  8. Angular - - $interpolate 和 $parse

    $interpolate 将一个字符串编译成一个插值函数.HTML编译服务使用这个服务完成数据绑定. 使用:$interpolate(text,[mustHaveExpression],[truste ...

  9. WMWARE下/sbin/scsi_id 返回值为空

    [root@HE1 ~]# /sbin/scsi_id -g -u /dev/sdb 第一种是从VMWARE层面去解决,用文本编辑器修改vmx文件,在vmx文件中任意位置(通常在最后)添加如下行: d ...

  10. TCMalloc

    一. 原理 tcmalloc就是一个内存分配器,管理堆内存,主要影响malloc和free,用于降低频繁分配.释放内存造成的性能损耗,并且有效地控制内存碎片.glibc中的内存分配器是ptmalloc ...