大话JS神器之Promise
前段时间的工作中,由于项目要在前端实现存储,于是便使用了websql,而websql的API涉及到了很多的异步问题,如果采取回调函数的方式处理,代码不够优雅,而且不利于理解,于是便找到了Promise,使用之后有一些自己的理解和心得,跟大家在本文中一起分享一下。
Promise为何物?
Promise中文释义为“誓言”、“承诺”之意,根据其音译,那就是“普罗米修斯”,这货很强大啊,在希腊神话中,是最具智慧的神明之一,最早的泰坦巨神后代,名字有“先见之明”(Forethought)的意思。
而JS中的Promise,其实就是ES6提供的一个对象,它扮演的就是“先知”这么个角色,它用来传递异步操作的消息,代表了某个未来才会知道结果的事件,并且这个事件提供统一的API,便于进一步处理,这个类目前在chrome32、Opera19、Firefox29以上的版本都已经支持。
接下来说说Promise规范,大家应该都不陌生了,因为这个规范已经出来很长一段时间了,目前在chrome32、Opera19、Firefox29以上的版本都已经支持Promise了,Promise规范的主要内容如下:
- Promise对象有三种状态:Pending(进行中)、Resolved(已完成)、Rejected(已失败),这三种状态只能从Pending转到Rejected或者Pending转到Rejected,不能逆向转换,而且状态一旦转换完成后,状态就凝固了,不能够再改变了。只有异步操作的结果,才能决定当前是哪一种状态,任何其他操作都无法改变这个状态,这点跟Deferred是不一样的。
- Promise模式唯一需要的一个接口是调用then方法,它可以用来注册当promise完成或者失败时调用的回调函数,then方法接受两个参数,第一个参数是成功时的回调,在promise由Pending态转换到Resolved态时调用,另一个是失败时的回调,在promise由Pending态转换到Rejected态时调用。
Promise有啥好处?
说了这么多,Promise到底有啥好处呢?能为我们的工作带来什么便捷呢?话不多说,是骡子是马拉出来遛遛,我们先new一个来玩玩:
var p = new Promise(function(resolve, reject){
//用setTimeout模拟异步操作
setTimeout(function(){
console.log('第一个Promise执行完成');
resolve('第一个Promise');
}, 2000);
});
上面的代码可以看出,new一个Promise对象时,要传入一个函数,而这个函数一般有两个参数,一个是resolve,另一个是reject,这两个参数不一定都要,但你要用的那个一定要传入,所以两个都写上就好了,这两个参数其实代表的是异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。而上面这段代码的意思就是用setTimeout模仿一个异步的操作,2秒后,在控制台打印“第一个Promise执行完成”,然后调用resolve方法,带一个参数:“第一个Promise”,运行代码,会直接输出“第一个Promise执行完成”,虽然我们只是new了一个Promise对象,但是函数当参数传进去的时候会被立即执行,所以直接输出,为了防止这种情况,我们一个将这段代码包在一个函数中:
function test1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('第一个Promise执行完成');
resolve('第一个Promise');
}, 2000);
});
return p;
}
如上,最后返回了一个Promise对象,接下来重头戏来了,Promise对象的then方法要出场了,它可以用来注册当Promise完成或者失败时调用的回调函数,我们接着上面的代码继续往下写:
test1().then(function(val){
console.log(val);
//后续操作 })
执行这段代码,两秒后,会在页面输出“第一个Promise执行完成”,紧接着输出“第一个Promise”。到这里,机智的你已经看出来,这个then不就是异步处理完成后,将传递过来的数据取到,然后进行后续操作,就是充当一个回调函数嘛,感觉这个Promise没有什么大不了的地方啊,只不过是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数而已,其实这种链式调用在一些比较复杂的页面中是很有必要的,因为回调函数如果比较少还好说,一旦多了起来而且必须讲究执行顺序的话,回调函数开始嵌套,那代码的恶心程度是简直无法忍受。而Promise的出现就是为了解决这个问题的。
Promise就提供了一种优雅的解决方案,主要用法就是将各个异步操作封装成好多Promise,而一个Promise只处理一个异步逻辑。最后将各个Promise用链式调用写法串联,在这样处理下,如果异步逻辑之间前后关系很重的话,你也不需要层层嵌套,只需要把每个异步逻辑封装成Promise链式调用就可以了。
Promise的基本API
话不多说,先用console.dir(Promise)打印出来看看:
从上图可以看出来主要有以下几个方法:
- Resolve:该方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作。
- Reject:该方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。
- Then: 所有的 Promise 对象实例里都有一个 then 方法,它是用来跟这个 Promise 进行交互的,then方法主要传入两个方法作为参数,一个 resolve 函数,一个 reject 函数,链式调用 ,上一个Promise对象变为resolved的时候,调用then中的Resolve方法,否则调用Reject方法,且then 方法会缺省调用 resolve() 函数。
- Catch:该方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说也可以写成then,但是用来捕获异常时,用catch更加便于理解。
- All:该方法可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。就是全部都执行完了才接着往下执行,例如:
var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("p1");
}, 2000);
}); var p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("p2");
}, 2000);
}); Promise.all([p1, p2]).then(function (result) {
console.log(result);
});
6. Race:竞速,类似All方法,它同样接收一个数组,不同的是只要该数组中的 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。就是只要某一个执行完了就接着往下执行。
Promise的链式调用
Then的链式调用才是Promise的意义所在,而要实现链式调用,在then中的resolve方法如何return很关键。在then方法中通常传递两个参数,一个 resolve 函数,一个 reject 函数。reject暂时不讨论,就是出错的时候运行的函数罢了。resolve 函数必须返回一个值才能把链式调用进行下去,而且这个值返回什么是有很大讲究的。
在then的resolve方法中返回一个新的Promise对象,如下代码:
function test1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('第一个Promise执行完成');
resolve('第一个Promise传来的值');
}, 2000);
});
return p;
} test1().then(function(val){
console.log('进入第一个then');
console.log(val);
var p1 = new Promise(function(resolve, reject){
setTimeout(function(){
console.log('第一个then执行完成');
resolve('第二个Promise传来的值');
}, 2000);
})
return p1; }).then(function(val){
console.log('进入第二个then');
console.log(val);
})
打印结果如下:
上述例子通过链式调用的方式,按顺序打印出了相应的内容。then 可以使用链式调用的写法原因在于,每一次执行该方法时总是会返回一个 Promise 对象,后一个then中的resolve方法要等到前一个then处理完了才执行。而返回一个新Promise之后再调用的then就是新Promise中的逻辑了。另外,在 then onFulfilled 的函数当中的返回值,可以作为后续操作的参数。
在then的resolve方法中返回一个值,如下代码:
function test1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('第一个Promise执行完成');
resolve('第一个Promise传来的值');
}, 2000);
});
return p;
} test1().then(function(val){
console.log('进入第一个then');
console.log(val);
setTimeout(function(){
console.log('第一个then执行完成');
}, 2000);
return '第二个Promise传来的值'; }).then(function(val){
console.log('进入第二个then');
console.log(val);
})
打印结果如下:
可以看出,第一个then中的resolve还未执行完,就进入了第二个then中,没有进行等待,是因为第一个then中返回的是一个字符串,而不是Promise对象,返回的值会传递到下一个then的resolve方法参数中。
总结
以上就是ES6 Promise的基本概念和用法,Promise还是非常赞的,如果你还在使用回调模式,我强烈建议你切换到 Promise,这样你的代码会变的更少,更优雅,并且更加容易理解,这样就不会等你开发完后,被那些读你代码的人在背后偷偷骂你。本文我自己理解结合网上的一些资料,希望你看完之后对你有帮助,有不足之处欢迎指正,谢谢~
大话JS神器之Promise的更多相关文章
- [转帖]APP逆向神器之Frida【Android初级篇】
APP逆向神器之Frida[Android初级篇] https://juejin.im/post/5d25a543e51d455d6d5358ab 说到逆向APP,很多人首先想到的都是反编译,但是单看 ...
- ORACLE恢复神器之ODU/AUL/DUL
分享ORACLE数据库恢复神器之ODU.DUL和AUL工具. ODU:ORACLE DATABASE UNLOADER DUL:DATA UNLOADER AUL:也称MyDUL 关于三种工具说明: ...
- python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器。
python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器. virtualenv virtualenv------用来建立一个虚拟 ...
- SSH客户端神器之 MobaXterm
SSH客户端神器之 MobaXterm 由于需要连接远程 Linux 服务器,早期使用过 Putty,SecureCRT,后面主要使用 Xshell. 自从接触了 MobaXterm之后,个人感觉比 ...
- 如何使用 js 实现一个 Promise.all 方法 PromiseAll
如何使用 js 实现一个 Promise.all 方法 PromiseAll Promise.all PromiseAll https://developer.mozilla.org/en-US/do ...
- JS 异步与 Promise
JS 异步与 Promise 本文写于 2020 年 6 月 8 日 1. 同步与异步与回调函数 Promise 现在是前端面试必考题呀,但是先不急着看 Promise,我们首先来看看什么是异步. - ...
- Vue调试神器之Vue.js devTools
Vue项目中使用Vue.js devTools这款调试神器,可以极大程度的提高我们的开发效率. 安装 1.打开Chrome网上应用商店安装插件(自墙),直接搜索devTools安装即可.贵宾传送阵,请 ...
- H5神器之canvas应用——网页修改保存图片
因为最近项目上的要求,需要在页面中可以对一张图片进行涂改和添加文字,然后再保存到(服务器)本地,因为也是第一次接触这方面的,然后爬网页啊爬网页,之后发现了一款adobe开发的一款插件,适合 Anroi ...
- JS 异步系列 —— Promise 札记
Promise 研究 Promise 的动机大体有以下几点: 对其 api 的不熟悉以及对实现机制的好奇; 很多库(比如 fetch)是基于 Promise 封装的,那么要了解这些库的前置条件得先熟悉 ...
随机推荐
- XML解析代码
import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.LinkedLi ...
- BZOJ 4522: [Cqoi2016]密钥破解
http://www.lydsy.com/JudgeOnline/problem.php?id=4522 题目:给你RSA密钥的公钥和密文,求私钥和原文,其中\(N=pq\le 2^{62}\),p和 ...
- [ python ] 使用sys模块实现进度条
在写网络IO传输的时候, 有时候需要进度条来显示当前传输进度,使用 sys 模块就可以实现: sys.stdout.write() 这个函数在在控制台输出字符串不会带任何结尾,这就意味着这个输出还没有 ...
- Delphi2007新功能 -- 有限的栈对象
今天使用Delphi2007,一个误输入,无意中发现Delphi2007的record类型居然能够和TObject一样定义方法和属性,而且不需要调用类似TObject.Create方法就能生成一个re ...
- 区块链开发(六)truffle使用入门和testrpc安装
在上篇博文中我们已经成功安装了truffle及所需相关环境,此篇就简单介绍一些truffle的使用及目录结构等. 简介truffle和testrpc truffle是本地的用来编译.部署智能合约的工具 ...
- activiti-ui源码构建
修改数据库链接:
- [BZOJ4537][Hnoi2016]最小公倍数 奇怪的分块+可撤销并查集
4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1474 Solved: 521[Submit][Stat ...
- AC日记——Collectors Problem uva 10779
UVA - 10779 思路: 最大流: s向所有的贴纸的种类连边,流量为Bob拥有的数量: 然后,Bob的朋友如果没有这种贴纸,则这种贴纸向bob的朋友连边,容量1: 如果bob的朋友的贴纸很多大于 ...
- (十二)MySQL逻辑备份mysqldump
(1)简介 语法 mysqldump -h服务器 -u用户名 -p密码 [-P端口号] [参数] 数据库名 >备份文件.sql 关于数据库: -A,--all-databases 所有库,会生成 ...
- Codeforces 1023 D.Array Restoration-RMQ(ST)区间查询最值 (Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Fi)
D. Array Restoration 这题想一下就会发现是只要两个相同的数之间没有比它小的就可以,就是保存一下数第一次出现和最后一次出现的位置,然后查询一下这个区间就可以,如果有0的话就进行填充. ...