入门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('异步操作成功了'); //
//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('异步操作成功了'); //
//reject('异步操作失败了'); //2
return '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('异步操作成功了'); //
}, 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('异步操作成功了'); //
}, 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('异步操作成功了'); //
}, 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('异步操作成功了'); //
//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方法。
入门Promise的正确姿势的更多相关文章
- windows系统下npm升级的正确姿势以及原理
本文来自网易云社区 作者:陈观喜 网上关于npm升级很多方法多种多样,但是在windows系统下不是每种方法都会正确升级.其中在windows系统下主要的升级方法有以下三种: 首先最暴力的方法删掉no ...
- GitHub 热点速览 Vol.18:刷 LeetCode 的正确姿势
作者:HelloGitHub-小鱼干 摘要:找对路子,事半功倍,正如本周 GitHub Trending #刷 LeetCode# 主题想表达的那般,正确的学习姿势方能让人走得更远,走进大厂
- 判断是否为gif/png图片的正确姿势
判断是否为gif/png图片的正确姿势 1.在能取到图片后缀的前提下 1 2 3 4 5 6 7 8 9 //假设这是一个网络获取的URL NSString *path = @"http:/ ...
- 在Linux(ubuntu server)上面安装NodeJS的正确姿势
上一篇文章,我介绍了 在Windows中安装NodeJS的正确姿势,这一篇,我们继续来看一下在Linux上面安装和配置NodeJS. 为了保持一致,这里也列举三个方法 第一个方法:通过官网下载安装 h ...
- 程序员取悦女朋友的正确姿势---Tips(iOS美容篇)
前言 女孩子都喜欢用美图工具进行图片美容,近来无事时,特意为某人写了个自定义图片滤镜生成器,安装到手机即可完成自定义滤镜渲染照片.app独一无二,虽简亦繁. JH定律:魔镜:最漂亮的女人是你老婆魔镜: ...
- ios监听ScrollView/TableView滚动的正确姿势
主要介绍 监测tableView垂直滚动的舒畅姿势 监测scrollView/collectionView横向滚动的正确姿势 1.监测tableView垂直滚动的舒畅姿势 通常我们用KVO或者在scr ...
- 玩转 Ceph 的正确姿势
玩转 Ceph 的正确姿势 本文先介绍 Ceph, 然后会聊到一些正确使用 Ceph 的姿势:在集群规模小的时候,Ceph 怎么玩都没问题:但集群大了(到PB级别),这些准则可是保证集群健康运行的不二 ...
- 解锁redis锁的正确姿势
解锁redis锁的正确姿势 redis是php的好朋友,在php写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为.这个时候我们就要用到锁.锁的方式有好几种,php不能在内存中用锁 ...
- jquery选中radio或checkbox的正确姿势
jquery选中radio或checkbox的正确姿势 Intro 前几天突然遇到一个问题,没有任何征兆的..,jquery 选中radio button单选框时,一直没有办法选中,后来查了许多资料, ...
随机推荐
- c#设计模式系列:命令模式(Command Pattern)
引言 命令模式,我感觉"命令"就是任务,执行了命令就完成了一个任务.或者说,命令是任务,我们再从这个名字上并不知道命令的发出者和接受者分别是谁,为什么呢?因为我们并不关心他们是谁, ...
- fhq treap——简单又好写的数据结构
今天上午学了一下fhq treap感觉真的很好用啊qwq 变量名解释: \(size[i]\)表示以该节点为根的子树大小 \(fix[i]\)表示随机权值 \(val[i]\)表示该节点的值 \(ch ...
- CH的电影推荐
1.推荐电影 张艺谋:一个都不能少 2.下载站点 TL95
- MySQL直接导出CSV文件,并解决中文乱码的问题
需求: 需要导出hr_users 表中的部分字段的数据,以前是用PHP写脚本,然后导出CSV文件. 在MySQL中,它自己就能导出CSV文件 ,只不过是有如下几个问题需要大家解决. 1. 生成文件不成 ...
- 网络CCNA基础了解
关于网络 CCNA.CCNP.CCIE 中的 CCNA 一.逻辑与.或.非 AND --> "与"计算 1 AND 1 = 1(取严) 1 AND 0 = 0 0 AND 1 ...
- samba使用
一. samba安装 1. 安装:apt-get install samba samba-common smbclient 安装成功后,会默认启动samba服务, 可用ps-ef | grep smb ...
- iOS核心动画之蒙版
应用场景 想让一些古卷文字慢慢渐变成背景色,而不是一个突兀的边界 layer的边框不是圆角,而是有星形框架的图片 方法 设置mask属性.这个属性也是一个layer属性,但只有alpha属性有效果.即 ...
- Numpy中扁平化函数ravel()和flatten()的区别
在Numpy中经常使用到的操作由扁平化操作,Numpy提供了两个函数进行此操作,他们的功能相同,但在内存上有很大的不同. 先来看这两个函数的使用: from numpy import * a = ar ...
- 2019 CCPC-Wannafly Winter Camp Day3(Div2, onsite)
solve 4/11 补题:5/11 A 二十四点* Code:pai爷 zz Thinking :pai爷 打表找规律,1张牌 10个不可能的 2张牌有 43 种不可能的 3张牌 有74 种不可能 ...
- Q678 有效的括号字符串
给定一个只包含三种字符的字符串:(,) 和 *,写一个函数来检验这个字符串是否为有效字符串.有效字符串具有如下规则: 任何左括号 ( 必须有相应的右括号 ). 任何右括号 ) 必须有相应的左括号 ( ...