前段时间的工作中,由于项目要在前端实现存储,于是便使用了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)打印出来看看:

从上图可以看出来主要有以下几个方法:

  1. Resolve:该方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作。
  2. Reject:该方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。
  3. Then: 所有的 Promise 对象实例里都有一个 then 方法,它是用来跟这个 Promise 进行交互的,then方法主要传入两个方法作为参数,一个 resolve 函数,一个 reject 函数,链式调用 ,上一个Promise对象变为resolved的时候,调用then中的Resolve方法,否则调用Reject方法,且then 方法会缺省调用 resolve() 函数。
  4. Catch:该方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说也可以写成then,但是用来捕获异常时,用catch更加便于理解。
  5. 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的更多相关文章

  1. [转帖]APP逆向神器之Frida【Android初级篇】

    APP逆向神器之Frida[Android初级篇] https://juejin.im/post/5d25a543e51d455d6d5358ab 说到逆向APP,很多人首先想到的都是反编译,但是单看 ...

  2. ORACLE恢复神器之ODU/AUL/DUL

    分享ORACLE数据库恢复神器之ODU.DUL和AUL工具. ODU:ORACLE DATABASE UNLOADER DUL:DATA UNLOADER AUL:也称MyDUL 关于三种工具说明: ...

  3. python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器。

    python三大神器之virtualenv   pip, virtualenv, fabric通称为pythoner的三大神器. virtualenv virtualenv------用来建立一个虚拟 ...

  4. SSH客户端神器之 MobaXterm

    SSH客户端神器之 MobaXterm 由于需要连接远程 Linux 服务器,早期使用过 Putty,SecureCRT,后面主要使用 Xshell. 自从接触了 MobaXterm之后,个人感觉比 ...

  5. 如何使用 js 实现一个 Promise.all 方法 PromiseAll

    如何使用 js 实现一个 Promise.all 方法 PromiseAll Promise.all PromiseAll https://developer.mozilla.org/en-US/do ...

  6. JS 异步与 Promise

    JS 异步与 Promise 本文写于 2020 年 6 月 8 日 1. 同步与异步与回调函数 Promise 现在是前端面试必考题呀,但是先不急着看 Promise,我们首先来看看什么是异步. - ...

  7. Vue调试神器之Vue.js devTools

    Vue项目中使用Vue.js devTools这款调试神器,可以极大程度的提高我们的开发效率. 安装 1.打开Chrome网上应用商店安装插件(自墙),直接搜索devTools安装即可.贵宾传送阵,请 ...

  8. H5神器之canvas应用——网页修改保存图片

    因为最近项目上的要求,需要在页面中可以对一张图片进行涂改和添加文字,然后再保存到(服务器)本地,因为也是第一次接触这方面的,然后爬网页啊爬网页,之后发现了一款adobe开发的一款插件,适合 Anroi ...

  9. JS 异步系列 —— Promise 札记

    Promise 研究 Promise 的动机大体有以下几点: 对其 api 的不熟悉以及对实现机制的好奇; 很多库(比如 fetch)是基于 Promise 封装的,那么要了解这些库的前置条件得先熟悉 ...

随机推荐

  1. [转载]基于Redis的Bloomfilter去重(附Python代码)

    前言: “去重”是日常工作中会经常用到的一项技能,在爬虫领域更是常用,并且规模一般都比较大.去重需要考虑两个点:去重的数据量.去重速度.为了保持较快的去重速度,一般选择在内存中进行去重. 数据量不大时 ...

  2. 【bzoj3439】KPM的MC密码

    这题乍一看后缀相等很烦的样子…… 其实如果把字符串倒过来,那么相等的后缀就可以转化成前缀,前缀相等扔进trie就可以了. 剩下无非是Trie的树链kth,主席树随便维护就好. 注意一个串彻底结束才能打 ...

  3. Nightmare安装and一个小例子

    前端的功能测试 官方说法A high-level browser automation library,翻译过来就是高级浏览器自动化库 常用于UI测试和爬网 功能测试必须在真正浏览器做,现在有四种方法 ...

  4. stylus安装以及使用命令行生成css文件

    Stylus 是一个CSS的预处理框架,2010年产生,来自Node.js社区,主要用来给Node项目进行CSS预处理支持,所以 Stylus 是一种新型语言,可以创建健壮的.动态的.富有表现力的CS ...

  5. ubuntu 16.04 qtcreator 闪退

    当用QtCreator 进行代码自动补全时,比如编写ros代码,ROS_INFO时候就会出现闪退,后面按照 http://doc.qt.io/qtcreator/creator-clang-codem ...

  6. Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果)

    Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果) 首句依然是那句老话,你懂得! finddreams :(http://blog.csdn.net/finddr ...

  7. windows上的命令telnet

    telnet可以测试端口号是否可用,比如: telnet ip:port 或者 telnet www.baidu.com PS:win7环境下,默认没有安装telnet客户端,你可以去“控制面板”-- ...

  8. 大数模板(Java)

    大数加法 /* 给出2个大整数A,B,计算A+B的结果. Input 第1行:大数A 第2行:大数B (A,B的长度 <= 10000 需注意:A B有可能为负数) Output 输出A + B ...

  9. java 时间格式化中的模式字母

    java日期格式化中的模式字母有特定的意义,由于没有注意,今天在做工程的时候导致出现了奇怪的日期: 错误写法如下: 然而得到了错误的结果: 正确写法如下: 这样就得到了正确的结果 mm指的是分钟,MM ...

  10. Linux命令之groupadd

    groupadd [选项] 组 创建一个新的组.Groupadd命令使用命令行中指定的值加上系统默认值创建新的组账户.新组将根据需要输入系统. (1).选项 -f,--force 如果指定的组已经存在 ...