大话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 封装的,那么要了解这些库的前置条件得先熟悉 ...
随机推荐
- Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】
转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...
- aiohttp/asyncio 小例子和解释
#!/usr/bin/env python # encoding: utf-8 import aiohttp import asyncio import time # 通过async def定义的函数 ...
- 【bzoj3289】mato的文件管理
首先允许离线,一眼莫队…… 然后考虑对于每次移动,这不就是让你求逆序对嘛(QAQ) 考虑怎么移动? 每次在最后添加一个数,比这个数大的数都会与其形成一个逆序对 每次在最后移除一个数,比这个数大的数都会 ...
- oracle 一行转多行
比如sql: select zyxdm from table where bindid=2265254 查询结果为:1|4|8|9|10 将这个查询结果转成多行,结果如下: ID 1 4 8 9 10 ...
- [ Python - 14 ] python进程及线程编程
什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派 ...
- win32 读取文本文件,并进行字符串分割和存储
//分割字符 char *p;//p存放临时行指针 q存放临时每一行的列指针 char *hang[100]={0};//存储第一行 char *lie[300]={0} ...
- codeforces 739D
这题码量好大…… 首先思考如何构造,不难找到一下两个条件 1. 在长度为i的环上的点一定是i的倍数个 2. 到达长度i的环的点集距离一定是连续的 第一个条件是很好搞的,关键是第二个条件. 因为存在着x ...
- 在Ubuntu 16.04安装 Let’s Encrypt并配置ssl
1.安装前准备 1)要确保python的默认版本为2.7及以上版本. 2)需要配置的apache.nginx需要提前配置绑定域名. 2.安装ssl 在这个https://certbot.eff.org ...
- Codeforces 702C Cellular Network(二分)
题目链接:http://codeforces.com/problemset/problem/702/C 题意: 在数轴上有N个城市和M个信号塔,给你这N个城市以及M个塔在数轴上的位置,求M个塔可以覆盖 ...
- hdu2665(主席树模板题)
hdu2665 题意 求区间第 k 小. 分析 参考 这类题目做法挺多的,例如 划分树. 这里使用主席树再写一发,不得不说主席树相比而言要好写的多,比起普通线段树,主席树就是复用了线段树共有的信息. ...