当前位置: 首页 > 编程日记 > 正文

入门Promise的正确姿势

Promise是异步编程的一种解决方案,从语法上说,Promise是一个对象,从它可以获取异步操作的消息。

Promise的基本用法

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供。

  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。
  • then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为 Reject时调用
var promise = new Promise(//异步执行,Promise对象创建后会被立即执行function (resolve,reject) {//耗时很长的异步操作if('异步处理成功') {  resolve();    //数据处理成功时调用} else {reject();    //数据处理失败时调用
    }}
)
//Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function A() {//数据处理成功后执行
    },function B() {//数据处理失败后执行
    }
)

下面我们举一个简单的例子来模拟一下异步操作成功和异步操作失败函数的运行过程。

console.log('starting');
var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("2秒后,我运行了");resolve('异步操作成功了');     //1//reject('异步操作失败了');    //2
     //return 'hello';       //3
}, 2000) }).then(function (value) { console.log('异步操作成功后执行我:',value); }, function (value) {console.log('异步操作失败后执行我:',value); } ) console.log('我也运行了');// 上面的代码中1处代码的调用,输出顺序是: //starting //我也运行了
//2秒后,我运行了 // 异步操作成功后执行我: 异步操作成功了// 上面的代码中2处代码的调用,输出顺序是: //starting //我也运行了
//2秒后,我运行了 // 异步操作失败后后执行我: 异步操作失败了


//上面的代码中3处代码的调用,输出顺序是:
//starting
//我也运行了
//2秒后,我运行了

知代码3处的return 'hello' 语句在新建的new Promise对象中并没有被当作参数返回给then()函数内.那么会不会返回给promise了呢?我们用一段代码来测试一下

console.log('starting');
var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("2秒后,我运行了");resolve('异步操作成功了');     //1//reject('异步操作失败了');    //2return 'hello';}, 2000) })
promise.then(function (value) { console.log('异步操作成功后执行我:',value);
},
function (value) {console.log('异步操作失败后执行我:',value);
}
)
console.log('我也运行了');
console.log(promise);
setTimeout(function () {console.log('5秒后,我执行了');console.log(promise);
},5000);//starting
//我也运行了
//Promise { pending }//[[PromiseStatus]]:"pending"//[[PromiseValue]]:undefined//__proto__:Promise {constructor: , then: , catch: , …}
//2秒后,我运行了
//异步操作成功后执行我: 异步操作成功了
//5秒后,我执行了
//Promise { resolved }//[[PromiseStatus]]:"resolved"//[[PromiseValue]]:"异步操作成功了"//__proto__:Promise {constructor: , then: , catch: , …}

由执行结果可知,变量promise仍然是new Promise对象的一个实例。所以return语句虽然被执行了,但对promise实例不会产生任何影响,相当于不存在。

由上面测试的代码可知,Promise对象有以下两个特点。
  (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled) 和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变 为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

resolve(value) VS resolve(Promise)

我们会在异步操作成功时调用resolve函数,其作用是将Promise对象的状态从Pending变为Resolved,并将异步操作的结果作为参数传递给then()方法里的第一个函数的形参

那么传入的参数是值和传入的参数是promise对象时有什么不同呢。我们来看一下例子。

当传入的参数为值时:

var time = new Date();
var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("1秒后,我运行了");resolve('异步操作成功了');     //1}, 2000) }).then(function (value) {console.log(value,new Date() - time);
})
//执行的输出结果为:
//2秒后,我运行了
//异步操作成功了 1002

大约过了一秒左右,我们可以看到在resolved状态的回调方法中,我们打印出了上面注释中的内容。我们能够通过resolve方法传递操作的结果,然后在回调方法中使用这些结果。

如果我们在resolve中传入一个Promise实例呢?

var time = new Date();
var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("2秒后,我运行了");resolve('异步操作成功了');     //1}, 2000) })var promise2 = new Promise(function (resolve,reject) {setTimeout(resolve,1000,promise);//setTimeout和setInterval中的第三个、及以后的参数会作为第一个参数func的参数传递给func函数
}).then(function (value) {console.log(value,new Date() - time);
})//执行后输出的结果为:
//
2秒后,我运行了 //异步操作成功了 2003

promise2经过了2秒后才打印出来结果。奇怪了,我们不是设置promise2经过1秒后执行吗?

简单说就是因为promise2中的resolve()函数传入了promise对象,此时promise对象的状态决定了promise的状态,同时会把返回值传给promise。

Promise/A+中规定 [[Resolve]](promise, x)

2.3.2.如果x是一个promise实例, 则以x的状态作为promise的状态

2.3.2.1.如果x的状态为pending, 那么promise的状态也为pending, 直到x的状态变化而变化。

2.3.2.2.如果x的状态为fulfilled, promise的状态也为fulfilled, 并且以x的不可变值作为promise的不可变值。

2.3.2.3.如果x的状态为rejected, promise的状态也为rejected, 并且以x的不可变原因作为promise的不可变原因。

2.3.4.如果x不是对象或函数,则将promise状态转换为fulfilled并且以x作为promise的不可变值。

Promise.prototype.then()

Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。 前面说过,then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。

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

var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("2秒后,我运行了");resolve('异步操作成功了');     //1}, 2000) })
promise.name = 'promise';
console.log('promise:',promise)
var promise2 = promise.then(function (value) {console.log(value);
})
promise2.name = 'promise2';
console.log('promise2:',promise2);// promise:
//     Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise"
//     __proto__:Promise {constructor: , then: , catch: , …}
// promise2:
// Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise2"
//     __proto__:Promise {constructor: , then: , catch: , …}

我们可以知道promise.then()方法执行后返回的是一个新的Promise对象。也就是说上面代码中的promise2是一个Promise对象,它的实现效果和下面的代码是一样的,只不过在then()方法里,JS引擎已经自动帮我们做了。

promise2 = new Promise(function (resolve,reject) {})

既然在then()函数里已经自动帮我实现了一个promise对象,但是我要怎么才能给resolve()或reject()函数传参呢?其实在then()函数里,我们可以用return()的方式来给promise2的resolve()或reject()传参。看一个例子。

//var time = new Date();
var promise = new Promise(function(resolve, reject) {  setTimeout(function() { console.log("2秒后,我运行了");resolve('异步操作成功了');     //1//reject('异步操作失败了');    //2}, 2000) })
//console.log('promise:',promise)
var promise2 = promise.then(function (value) {console.log(value);//resolve('nihao');  //报错。注意,这里不能写resolve()方法,因为在then函数里是没有resolve方法的return (1);       //return promise;   //也可以return一个promise对象,返回promise对象执行后resolve('参数值')或reject('参数值')内部的参数值
  //如果不写return的话,默认返回的是return undefined 。
}) var promise3 = promise2.then(function (value) {console.log('is:',value); },function (value) {console.log('error:',value); }) promise2.name = 'promise2'; setTimeout(function () {console.log(promise2); },3000);//2秒后,我运行了 //异步操作成功了 //is: 1 //Promise {resolved}//name:"promise2"//__proto__:Promise//[[PromiseStatus]]:"resolved"//[[PromiseValue]]:1

Promise与错误状态处理

.then(null, rejection),用于指定异步操作发生错误时执行的回调函数。下面我们做一个示例。

var promise = new Promise(function(resolve, reject) {setTimeout(function () {reject('error');},2000);
}).then(null,function(error) {console.log('rejected', error)
});
//rejected error

我们知道then()方法执行后返回的也是一个promise对象,因此也可以调用then()方法,但这样的话为了捕获异常信息,我们就需要为每一个then()方法绑定一个.then(null, rejection)。由于Promise对象的错误信息具有“冒泡”性质,错误会一直向后传递,直到被捕获为止(向后传递指的是:后面的then函数会一直执行rejected下的代码,并抛出第一个then函数/Promise执行出错时的信息)。因此Promise为我们提供了一个原型上的函数Promise.prototype.catch()来让我们更方便的捕获到异常。

我们看一个例子

var promise = new Promise(function(resolve, reject) {setTimeout(function () {reject('error');},2000);
}).then(function(value) {console.log('resolve', value);
}).catch(function (error) {console.log(error);
})
//运行结果
//error

上面代码中,一共有二个Promise对象:一个由promise产生,一个由then产生。它们之中任何一个抛出的错误,都会被最后一个catch捕获。

但是如果用.then(null, rejection)方法来处理错误信息,我们需要在每一个rejection()方法中返回上一次异常信息的状态,这样当调用的then()方法一多的时候,对会对代码的清晰性和逻辑性造成影响。

所以,一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法。

转载于:https://www.cnblogs.com/yuliangbin/p/8592724.html

相关文章:

雪花算法 Java 版

雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID 各 bit 含义: 1 bit: 符号位,0 是正数 1 是负数, ID 为正数,所以恒取 041 bit: 时间差,我们可以选择一个参考点,用它来计算与当前时间的时间差…

【matlab】第二次上机课

1、采用complex建立一个复数数组和直接创立一个复数数组,并计算它们的幅度。 代码实现: a complex(1,2);b 1 3i;length1 abs(a)lengthe abs(b)重点: 使用comlex创建复数 用abs求幅度(模长) 2、将A[0.8147, 0…

运行代码功能尝试

首先定义个文本域并且给个ID <textarea id"O_txt_1" rows"8" cols"80"> <!--要运行的代码--> </textarea> 然后定义个按钮 <input type"button" value"运行代码" οnclick"runCode(O_txt_1)&qu…

删除当前及子文件夹中的空目录

在对文件进行操作的工程中不免会出现空目录的情况&#xff0c;你想怎么去删除那些空目录一个一个去找&#xff0c;然后删除&#xff1f;不会吧&#xff0c;这也太累了&#xff0c;用批处理吧&#xff0c;帮你提高工作效率的&#xff0c;它会准确的判断然后进行删除。 echo off …

基于WebSocket实现聊天室(Node)

基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议&#xff0c;服务端可以主动向前端传递数据&#xff0c;相比比AJAX轮询服务器&#xff0c;WebSocket采用监听的方式&#xff0c;减轻了服务器压力 本文作为学习websocket的练习&#xff0c;实现在线聊天的功能…

Ruby 之 Block, Proc, Lambda 联系--区别,转载

Ruby 之 Block, Proc, Lambda Block Block 不是对象&#xff0c;是Ruby的语言特性&#xff0c;近似于闭包&#xff08;Closure&#xff09;。 范例&#xff1a; def meth res yield "Block called returns #{res}"endputs meth do next “next_value” end #…

【java】牛客网刷题

1、 给出以下代码 public class TestObj{public static void main(String[] args){Object onew Object(){public boolean equals(Object obj){return true;}};System.out.println(o.equals(“Fred”));}}答案&#xff1a; true 总结&#xff1a; 知识点&#xff1a; &…

Winder摆杆不稳除了PID还可能的原因

1.卷径计算有问题。 2.速度限制住了。 转载于:https://www.cnblogs.com/Lion-Ming/p/11104972.html

javascript断点调试方法

http://www.blogguy.cn/show-728-1.html

Python爬虫案例-获取最新的中国行政区域划分

源网页&#xff1a;中国统计局标准 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/ 打开网页后可以分析出行政区域划分共分为5层 根据传入参数&#xff0c;生成网页地址时需要1-3层的只传本身以及 4层及以后的增加当前省份的前缀。 #生成实际需要解析的页面地址 def …

管理分布式session的四种方式。

应用服务器的高可用架构设计最为理想的是服务无状态&#xff0c;但实际上业务总会有状态的&#xff0c;以session记录用户信息的例子来讲&#xff0c;未登入时&#xff0c;服务器没有记入用户信息的session访问网站都是以游客方式访问的&#xff0c;账号密码登入网站后服务器必…

【matlab】第三章数组和数组的运算

&#xff08;一&#xff09;操作练习 1、构建等差数列的方法 代码实现 //方法1A 5:1:10//输出结果A 5 6 7 8 9 10//方法2 A linspace(1,10,3) //输出结果 A 1.0000 5.5000 10.0000 //注意最后的3指的是一共三个元素//等比数列A logspace(0,2,5)//输…

用PHP生成等比图像的方法

PHP代码 <?php /************************************************************************ * 函数名称&#xff1a;createSmallImg() * 函数说明&#xff1a;创建等比例图片 * 输入参数&#xff1a;$dir 保存路径$source_img 原图片名称$small_ex 缩率图文件名后缀$maxw…

ARM7启动代码

1:PRESERVE8: Reguire8和Preserve8 C和汇编有8位对齐的要求&#xff0c;这两个伪指令可以满足此要求&#xff0c;存在REQUIRE8<——> PRESERVE8的对应关系&#xff0c;但不是说有一个REQUIRE8就要有一个 PRESERVE8&#xff0c;如果是一个c文件和一个汇编文件的调用&#…

一次完整请求的日志

一次完整请求的日志&#xff1a;各种配置文件&#xff1a;spring-mvc.xml<?xml version"1.0" encoding"UTF-8"?><beans xmlns"http://www.springframework.org/schema/beans" rel"nofollow"" target"_blank"…

Aveva Marine C# 二次开发入门001

1# 引用 C:\AVEVA\Marine\OH12.1.SP4\Aveva.ApplicationFramework.dll C:\AVEVA\Marine\OH12.1.SP4\Aveva.ApplicationFramework.Presentation.dll 2# 引用命名空间&#xff0c; using Aveva.ApplicationFramework.Presentation;using Aveva.ApplicationFramework; 3# 继承接口…

搜集《ASP.NET中常用的26个优化性能方法》

1. 数据库访问性能优化    a.数据库的连接和关闭    访问数据库资源需要创建连接、打开连接和关闭连接几个操作。这些过程需要多次与数据库交换信息以通过身份验证&#xff0c;比较耗费服务器资源。ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响…

【matlab】我要自学网笔记总结 1.3

1.3 1、在matlab的命令行窗口可以直接进行数学运算 2、π 和平方的使用 S pi*r^2 3、如果输入一个多位小数&#xff0c;输出时只显示小数点后四位&#xff0c;但计算的时候使用的是真实值。 如果要改变显示的位数 &#xff08;1&#xff09;可以在 预设 - 命令行窗口 - 数值…

IT规划的企业应用实践(6)研究背景 之 企业信息化建设的诉求

研究背景 之 企业信息化建设的诉求 从实践角度看&#xff0c;企业信息化建设的诸多问题和诉求&#xff0c;可以归纳为以下几个方面&#xff1a; 1. IT系统本身&#xff1a; l 面对成本的压力和客户的要求&#xff0c;在流程方面、运作方面离不开IT支持&#xff0c;IT系统如何支…

Codeforces Gym100812 L. Knights without Fear and Reproach-扩展欧几里得(exgcd)

补一篇以前的扩展欧几里得的题&#xff0c;发现以前写错了竟然也过了&#xff0c;可能数据水&#xff1f;&#xff1f;&#xff1f; 这个题还是很有意思的&#xff0c;和队友吵了两天&#xff0c;一边吵一边发现问题&#xff1f;&#xff1f;&#xff1f; L. Knights without F…

Tarjan无向图连通性

割点&#xff1a;去掉某点x&#xff0c;该无向图分割成两部分&#xff08;及以上&#xff09; 割边&#xff1a;去掉某条边x&#xff0c;该无向图分割成两部分&#xff08;及以上&#xff09; 时间戳&#xff1a;在搜索树上的遍历序号dfn 追溯值&#xff1a;subtree子树和非搜索…

php去除字符串首尾空格(包括全角)(转)

<? $str" dfdfdf曊壷顳 道德观第三附属 "; $str mb_ereg_replace(^( | ), , $str); $str mb_ereg_replace(( | )$, , $str); echo mb_ereg_replace( , "\n ", $str); ?>转载于:ht…

【单片机】写电子钟时遇到的问题

1、<> 与""的区别 1、<> 先去系统目录中找头文件&#xff0c;如果没找到再去当前目录下找。 所以一般用于向标准的头文件如 studio.h 和 stdlib.h 等方法。 2、"" 首先在当前目录下寻找&#xff0c;如果找不到在去系统目录下寻找。这个用于自…

什么是业务组件?

海浪给大家分享了一些关于业务组件的文章&#xff0c;但是什么是业务组件呢&#xff1f;海浪转载了yongtree写的这篇文章。业务组件是一系列不可分割的业务活动&#xff0c;是构建专业化企业的功能模块。业务组件的优势在很大程度上来源于其具备两个相关但截然不同的特性&#…

3.3.2 函数参数不得不说的几件事

函数定义时圆括号内是使用逗号分隔开的形式参数列表&#xff08;parameters&#xff09;&#xff0c;一个函数可以没有参数&#xff0c;但是定义和调用时一对圆括号必须要有&#xff0c;表示这是一个函数并且不接受参数。函数调用时向其传递实参&#xff08;arguments&#xff…

wpf 对控件进行截图,获取快照

有时候我们项目&#xff0c;在执行某个操作后&#xff0c;会生成一些数据结果&#xff0c;如报表一类的东西&#xff0c;我们需要对结果进行保存&#xff0c;甚至是生成word文档。 那么首先获取到控件快照就最基本的条件。 生成快照的静态方法类 using System; using System.Co…

【java】兴唐第二十一节(LinkedList和泛型)

LinkedList知识点 1、实现了Iterable接口的类具有迭代功能。 2、List接口为Collection的子类&#xff0c;表示线形数据列表&#xff0c;其实现类有&#xff1a;ArrayList(数组线性表)与LinkedList(链表) 算了不多说了&#xff0c;上图吧 3、ArrayList是一个可变数组&#xff…

Elgg网站迁移指南

转载地址&#xff1a; http://blog.sina.com.cn/s/blog_84068de60100vr21.html Elgg官方文档上的网站迁移部分是有问题的——缺少了一些重要步骤&#xff0c;而且过程更麻烦。正确的方法如下&#xff1a; 备份网站文件&#xff0c;包括uploads文件夹导出数据库在数据库文件中&a…

INFO:在InstallShield中修改安装包压缩.cab包的大小

如果我们用InstallShield打包一个数据非常大的安装包&#xff08;Basic MSI和InstallScript MSI工程类型&#xff09;&#xff0c;默认情况下安装包会产生多个.cab文件&#xff0c;这里简单说明我们如何修改安装包中.cab文件的大小。首先&#xff0c;有个信息大家需要知道&…

MEF依赖注入实例

什么是MEF 先来看msdn上面的解释&#xff1a;MEF(Managed Extensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展&#xff0c;而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码&#xff0c;避免生成脆弱的硬…