只为那句承诺-大话Promise

开发 前端
Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

[[175061]]

大家周末好,要说最近几年什么语言大红大紫,当属JavaScript了。话说虽然是10天就创造出的语言,但是人家能文能武。web前端自然不必多说了,各种框架你方登罢我上场,前两年还是Angular一统天下,这两年React又是大红大紫,还有Vue最近异军突起,好不红火。要是仅仅是前端也就算了,但是由于Node.js人家在后台也能写,React Native的出现让人家移动端也能做。好吧,还有硬件上也出现Ruff方案,好像硬件上也能写了。真是让人感觉挺有意思的事情。

图表君上边叨叨了这么多,难道是为JavaScript唱赞歌的吗?呵呵,其实并不是。只是最近因为在用上篇文章介绍的AWS Lambda。Lambda现在只支持Java,Node.js,Python。最终选择了Node.js进行开发,不可避免的要牵扯到异步操作的问题。那么今天就来聊聊JavaScript中的Promise。

什么是Promise

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

上面是Promise的一个定义,引自阮一峰的ES6标准入门一书。S6标准入门。多说一句,目前的JavaScript项目无论是前台或者是后台,都应该采用ES6的标准语法来写,ES6让JavaScript的书写更加的清晰和规范。

基本用法

如何来构造一个promise对象呢?ES6中提供了原生Promise可以使用。

  1. var promise = new Promise(function(resolve, reject) { 
  2.   // ... here is some code 
  3.  
  4.   if (/* 异步操作成功 */){ 
  5.     resolve(value); 
  6.   } else { 
  7.     reject(error); 
  8.   } 
  9. });  

上面的例子给出了new一个promise对象的方法,Promise的构造函数接受一个函数作为参数传入,这个函数的两个参数,reject和resolve是JavaScript本身提供的两个函数。一个promise对象有三个状态分别是,pending,resolved,rejected。resolve函数可以将pending状态转变为resolved状态。reject函数可以讲pending状态转变了rejected状态。对象的状态不受外界的影响,同样也是promise名字的由来。外部你拿着我的一个承诺,一会我会告诉你我的状态。

promise对象通过then方法来添加回调函数。例如这样

  1. promise.then(data=> console.log(data), err=> console.log(err)); 

当promise被resolved的时候,就会把data log出来。当promise被rejected的时候,err就会被log出来。看上去好像是挺简单的,的确Promise的应用使得异步的操作,以同步的形式表现出来。当发生错误的时候可以通过catch方法,来定义回调函数。

怎么用

上边都是一些干巴巴的定义,那么到底该怎么用呢?Promise又怎么样的解决了问题呢,下边我们看一个例子。假设下边一个场景,我们一个服务,从一个外边service获取数据,然后写到一个db里,或者一个存储里,***在把存储的状态龙出来,那么如果没有promise是怎么写的呢?可能会是这样。 

  1. getData(function (value1) { 
  2.   storeToDb(value1, function(value2) { 
  3.     logStore(value2, function(value3) { 
  4.       //... 
  5.     }); 
  6.   }); 
  7. });  

传统的回调的写法,这样使得代码逻辑混乱在一起。再想想如果再加上错误处理的情况,更是酸爽。那么用promise来写会怎么样呢?看下边这样的代码

  1. function getData(){ 
  2.     return new Promise((resolve,reject) =>{ 
  3.         // ... send request to get data 
  4.          
  5.         if(/* get successfully*/){ 
  6.             resolve(data) 
  7.         }else
  8.             reject(err) 
  9.         } 
  10.     }) 
  11.  
  12. function storeData(data){ 
  13.     return new Promise((resolve,reject)=>{ 
  14.         // ... store the data 
  15.          
  16.         if(/*store successfully*/){ 
  17.             resolve(data) 
  18.         }else
  19.             reject(err) 
  20.         } 
  21.     }) 
  22.  
  23.  
  24. getData() 
  25.     .then(data => storeData(data)) 
  26.     .then(data => console.log('the process is done',data)); 
  27.     .catch(err => console.error('there is the err',err));  

这样写是不是就是很清楚了,先getData,然后再storeData,***将这次运行的情况log了出来,其中有任何的问题,在catch中都可以Catch出来。代码的逻辑以同步的方式得到了体现。我们来看看如果是其他语言会怎么写,下边是个ruby的语言的例子 

  1. def get_data  
  2.     // ...send request 
  3.      
  4.     if /*get successfully */ 
  5.         return data 
  6.     else 
  7.         raise GetDataError 
  8.     end 
  9. end 
  10.  
  11.  
  12. def store_data 
  13.     // ...save to db 
  14.      
  15.     if /*save successfully */ 
  16.         return data 
  17.     else 
  18.         raise StoreDataError 
  19.     end 
  20. end 
  21.  
  22.  
  23. /*Main Logic*/ 
  24. begin 
  25.     request_data = get_data 
  26.     db_data = store_data request_data 
  27.     p "here is the store data #{db_data}" 
  28. rescue e 
  29.     p "here is some errors #{e}" 
  30. end  

我们对比两个例子,可以看到在使用的Promise后让JavaScript的异步方式的编程模式更将清楚,也更加让人容易理解。

由于JavaScript的执行环境是单线程的,所以大量采用了异步的方式来进行编程,这使得我们写起代码并不十分符合我们一般的习惯。但是Promise的出现让这种问题能得到一定程度的缓解。

但是异步操作异步操作的好处,比如上边的那个例子,如果我们想要做的同时并发10个操作,那个在ruby或者其他语言中中就要启多个线程来进行。但是JavaScript就完全没有这个问题。只要简单的loop下就行了。

但是如果我们想要在这10个操作完成后根据返回的状态做点其他操作该怎么做呢?这时候用Promise.all就是***的了。

  1. let p = Promise.all([p1, p2, p3]); 

Promise.all接受数组作为参数传入,每个元素都是一个promise对象。只要所有子promise都resolved以后,p才会被resolved。只要有一个被rejected,这个p就会被rejected。但是有一点是这些子promise之间并不会有顺序的关系。再来看一个例子:

  1. var guid = 0; 
  2. function run() { 
  3.   guid++; 
  4.   var id = guid; 
  5.   return new Promise(resolve => { 
  6.     setTimeout(function () { 
  7.       console.log(id); 
  8.       resolve(id); 
  9.     }, (Math.random() * 1.5 | 0) * 1000); 
  10.   }); 
  11.  
  12. var promises = Array.from({ length: 10 }, run); 
  13. Promise.all(promises)  

OUTPUT:

  1. 10 
  2.  

从这次的output可以看到,promise之间并没有顺序执行,实际上是并发的。那么如何让这些promise是顺序执行呢?留个大家自己思考下,下篇文章,我们揭晓。或者可以联系图表君,私下告诉你答案哦。 ps,当然也可以用一些第三方的库和方案,例如(async)来实现顺序操作,但是代码的乐趣不就是做些思维挑战吗:)

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2018-04-09 08:17:36

线程ThreadLocal数据

2023-09-15 15:31:23

异步编程Promise

2022-09-12 06:35:00

C++协程协程状态

2022-08-08 10:45:49

PromiseJava脚手架

2009-06-02 16:58:56

运维管理配置摩卡

2017-02-14 08:36:56

2011-12-02 08:51:19

PHP

2012-09-10 15:57:58

云计算混合云私有云

2016-07-07 11:37:04

灾备

2016-08-24 11:13:30

2020-12-15 08:01:24

Promise参数ES6

2020-07-29 17:35:08

Promise源码前端

2009-10-26 15:18:39

HTML 5

2010-07-06 08:56:54

JBoss ToolsEclipse 3.6

2015-11-12 10:09:47

2013-11-26 15:44:25

Android设计模式

2011-12-19 14:28:14

Java设计模式

2012-08-22 13:53:15

Windows 8Linux

2009-08-10 17:27:36

2015-05-27 10:36:57

光通信
点赞
收藏

51CTO技术栈公众号