当前位置: 首页 > 科技观察

就为了那个promise——大话王Promise

时间:2023-03-17 23:57:36 科技观察

大家周末愉快,要说这几年流行的语言是JavaScript。据说,语言虽然是10天创造出来的,却能文能武。Web前端自然不用多说。各种框架你可以用,我来玩。在过去的两年里,Angular称霸了世界。近两年,React又火了一把,Vue最近又异军突起,所以风生水起。如果只是前端还好,但是因为Node.js的人也可以在后台写,ReactNative的出现让人可以在移动端做。嗯,硬件上也出现了Ruff方案,看来也可以写在硬件上了。这真的很有趣。Chart君说了这么多,是在为JavaScript唱赞歌吗?哈哈,不是。只是因为最近用的是上篇文章介绍的AWSLambda。Lambda目前仅支持Java、Node.js和Python。最终选择了Node.js进行开发,这就不可避免地涉及到异步操作的问题。那么今天我们来谈谈JavaScript中的Promise。什么是承诺?Promise是一种针对异步编程的解决方案,比传统的解决方案——回调函数和事件更合理、更强大。它是由社区首先提出并实施的。ES6将其写入语言标准,统一使用,原生提供Promise对象。所谓Promise,简单来说就是一个容器,里面装的是将来会结束的事件(通常是异步操作)的结果。从句法上讲,Promise是一个对象,可以从中获取异步操作的消息。Promise提供了统一的API,各种异步操作可以统一处理。以上是Promise的定义,引用自阮一峰的ES6标准介绍。S6标准入门。还有一点,现在的JavaScript项目,不管是前台还是后台,都应该使用ES6的标准语法来写。ES6使得JavaScript的书写更加清晰和规范。如何在基本用法中构造一个promise对象?ES6提供了可以使用的原生承诺。varpromise=newPromise(function(resolve,reject){//...hereissomecodeif(/*异步操作成功*/){resolve(value);}else{reject(error);}});上面的例子给出了一个promise对象的new方法。Promise的构造函数接受一个函数作为参数。这个函数的两个参数reject和resolve是JavaScript自己提供的两个函数。promise对象具有三种状态,pending、resolved和rejected。resolve函数可以将pending状态更改为resolved状态。reject函数可以将pending状态更改为rejected状态。对象的状态不受外界影响,这也是promise名字的由来。在外面你对我有一个承诺,一会儿我会告诉你我的状态。Promise对象通过then方法添加回调函数。例如,promise.then(data=>console.log(data),err=>console.log(err));当承诺得到解决时,数据将被注销。当promise被拒绝时,err将被注销。看似很简单,确实Promise的应用使得异步操作可以用同步的形式来表达。当发生错误时,可以通过catch方法定义一个回调函数。如何使用上面都是一些干巴巴的定义,那么如何使用呢?Promise是如何解决问题的?让我们看下面的例子。假设如下场景,我们有一个服务从外部服务获取数据,然后写入到db或者storage中,而***是拉出storage状态,那么如果没有promise我们怎么写?也许会是这样。getData(function(value1){storeToDb(value1,function(value2){logStore(value2,function(value3){//...});});});传统的回调写法让代码逻辑混乱在一起。再想想,如果再加上错误处理,那就更酸了。那么如果你用promise写它会发生什么?看下面的代码:functiongetData(){returnnewPromise((resolve,reject)=>{//...sendrequesttogetdataif(/*getsuccessfully*/){resolve(data)}else{reject(err)}})}functionstoreData(数据){returnnewPromise((resolve,reject)=>{//...storethedataif(/*storesuccessfully*/){resolve(data)}else{reject(err)}})}getData().then(data=>storeData(data)).then(data=>console.log('theprocessisdone',data));.catch(err=>console.error('thereistheerr',err));这样写是不是很清楚了,先getData,再storeData,***会把这次的运行情况记录出来,如果有什么问题可以在catch中捕获。代码的逻辑以同步方式体现。让我们看看用其他语言是如何写的。下面是一个ruby语言的例子=get_datadb_data=store_datarequest_datap"hereisthestoredata#{db_data}"rescueep"hereissomeerrors#{e}"end我们对比两个例子,可以看出使用Promise后,异步编程模式JavaScript会更清晰有趣易懂。由于JavaScript的执行环境是单线程的,大量使用异步方法进行编程,使得我们在编写代码时不太符合我们一般的习惯。但是Promise的出现可以在一定程度上缓解这个问题。但是异步操作的好处,比如上面的例子,如果我们想同时做10个并发操作,在ruby或者其他语言中,我们需要启动多个线程来做。但是JavaScript完全没有这个问题。只需一个简单的循环即可。但是如果我们在这10个操作完成后还想根据返回的状态做其他操作呢?这时候最好使用Promise.all。letp=Promise.all([p1,p2,p3]);Promise.all接受一个数组作为参数,每个元素都是一个promise对象。只要所有子承诺都得到解决,p就会得到解决。只要有一个被拒绝,这个p就会被拒绝。但有一件事是这些子承诺之间没有顺序关系。让我们看另一个例子:varguid=0;functionrun(){guid++;varid=guid;returnnewPromise(resolve=>{setTimeout(function(){console.log(id);resolve(id);},(Math.random()*1.5|0)*1000);});}varpromises=Array.from({length:10},run);Promise.all(promises)OUTPUT:23567810149从这个输出我们可以看出promises之间有不是顺序执行,实际上是并发的。那么如何让这些承诺顺序执行呢?留给大家自己去思考,我们会在下一篇文章中揭晓。或者可以联系图君,私信告诉你答案。ps,当然你也可以使用一些第三方的库和解决方案,比如(async)来实现顺序操作,但是代码的乐趣不在于做一些思维挑战:)