锋利的ES6 — Promise
锋利的ES6 系列文章介绍ES6(ES5+)中新增的比较好用的新特性。本篇聊聊Promise。
目录
异步处理
在Javascript的世界里,所有代码都是单线程执行的。
由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现,但是如果代码中异步代码比较多,也就意味着需要写很多回调函数,这样的代码难以读懂,维护困难,而且不利于复用。
我们希望不用写太多回调,而是也可以像同步代码一样链式执行,而Promise就是为了这种情况而生的。
Promise介绍
古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象,Promise是对将来会执行的代码的处理。
Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。
Promise本身并不能实现任何业务,我们之所以要使用Promise只是为了不写回调,为了把本来异步的代码写成同步的形式,我们先来看一个简单的Promise处理异步的例子:承诺一秒之后生成的随机数大于0.5
1 | const pro = new Promise((resolve, reject) => { |
变量pro
是一个Promise对象,它负责执行参数里的函数,函数带有 resolve
和 reject
两个参数,resolve
和 reject
函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
注意:resolve或reject调用的时候自定义参数只能传一个,如果要传递多个则使用对象的方式传递。
Promise内部只负责判断和修改状态,并不需要执行具体的逻辑,承诺兑现的逻辑在then
里执行,承诺失信的逻辑在catch
里执行。
Promise详解
创建Promise对象
1 | var pro = new Promise( executor ) |
executor: 是一个函数,该函数在创建Promise对象的同时被调用执行。
executor语法:function(resolve, reject) {…}
- resolve:将Promise对象状态修改为 fulfilled,可以传递参数到then方法的第一个函数中
- reject:将Promise对象状态修改为 rejected,可以传递参数到 then 方法的第二个函数或catch方法的第一个函数中
Promise有三种状态
- Pending(进行中,初始状态,既不是成功,也不是失败状态)
- Resolved(又称 Fulfilled,意味着操作成功完成)
- Rejected(意味着操作失败)
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
因为 Promise.prototype.then
和 Promise.prototype.catch
方法返回promise 对象, 所以它们可以被链式调用。
Promise改造ajax
Promise更多的时候都是用于处理ajax请求
1 | new Promise((resolve, reject) => { |
这样就可以在then
里处理请求成功之后的逻辑,不需要写回调函数了。
嵌套Promise展开
我们再来看一个例子
1 | new Promise((resolve, reject) => { |
此时最好将其展开,也是一样的结果,而且会更好读
1 | new Promise((resolve, reject) => { |
第一个then返回的promise实例承诺兑现可以在第二个then里继续处理,这样的链式操作更加清爽
Promise.all
这个方法返回一个新的promise对象,该promise对象在数组里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。
1 | var promise1 = Promise.resolve(3); |
Promise.race
Promise.race() 类似于Promise.all() ,区别在于它有任意一个完成就算完成。
1 | let p1 = new Promise(resolve => { |
Promise.resolve
1 | const pro = Promise.resolve(123); |
Promise.reject
1 | const pro = Promise.reject(new Error('abc')); |
微任务和宏任务
我们先来看这样一道题
1 | setTimeout(function(){ |
先不说答案,先说概念
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。这句话读起来有些生涩,让我们来分析一下上面代码的运行顺序
这段代码作为宏任务,进入主线程
先遇到
setTimeout
,那么将其回调函数注册后分发到宏任务异步队列里接下来遇到了
Promise
,new Promise
立即执行,then
函数分发到微任务异步队列里遇到
console.log('2')
,立即执行整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了
then
在微任务队列里面,所以这时then
里的console.log('3')
执行ok,第一轮
Event Loop
结束了,我们开始第二轮,当然要从宏任务队列开始。我们发现了宏任务中setTimeout
对应的回调函数,立即执行console.log('1')
。所以,这道题的最终执行结果为 2、3、1
总结一下:js代码分为宏任务和微任务,宏任务包括整体代码script,setTimeout,setInterval,微任务包括Promise,process.nextTick。代码首先进入整体宏任务,整体宏任务执行完成以后开始执行微任务,当所有微任务完成以后再次查找宏任务,然后依次循环执行。
代码不是万能的,但不写代码是万万不能的
Dary记
-
更多干货,尽在公众号
转载请注明来源,文末有原始链接。欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 dary1112@foxmail.com
创作不易,您的打赏是我更新的动力
-
支付宝
-
微信
文章标题:锋利的ES6 — Promise
文章字数:1.8k
本文作者:Dary
发布时间:2020-03-19, 16:23:00
最后更新:2020-03-19, 16:32:47
原始链接:http://www.xiongdalin.com/2020/03/19/ES6-promise/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。
Built By Dary