ES6系列--Promise最全整理
栏目: JavaScript · 发布时间: 5年前
内容简介:曾几何时,我们为了获取异步调用的结果,不得不大量使用回调函数,我们看下面这个例子:通过Jquery的ajax获取服务器数据当需要发送多个异步请求,且每个请求之间需要相互依赖时,我们只能以嵌套的方式来解决
曾几何时,我们为了获取异步调用的结果,不得不大量使用回调函数,我们看下面这个例子:
通过Jquery的ajax获取服务器数据
let url1 = 'http://xxx.xxx.1'; $.ajax({ url:url1, error:function (error) {}, success:function (data1) { console.log(data1); } }); 复制代码
当需要发送多个异步请求,且每个请求之间需要相互依赖时,我们只能以嵌套的方式来解决
let url1 = 'http://xxx.xxx.1'; let url2 = 'http://xxx.xxx.2'; let url3 = 'http://xxx.xxx.3'; $.ajax({ url:url1, error:function (error) {}, success:function (data1) { console.log(data1); $.ajax({ url:url2, data:data1, error:function (error) {}, success:function (data2) { console.log(data2); $.ajax({ url:url3, data, error:function (error) {}, success:function (data3) { console.log(data3); } }); } }); } }); 复制代码
在处理多个异步逻辑时,就需要多层的回调函数嵌套,也就是我们常说的回调地狱。
这种编码模式的主要问题:
- 代码臃肿。
- 可读性差。
- 耦合度过高,可维护性差。
- 代码复用性差。
- 容易滋生 bug。
- 只能在回调里处理异常。
而Promise的出现,就是为了解决回调函数所引出的各种问题
什么是Promise
Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。
- 从语法上讲,promise 是一个对象,从它可以获取异步操作的消息
- 从本意上讲,它是承诺,承诺它过一段时间会给你一个结果
代码书写比较
首先封装一个支持 Promise 的 ajax 方法,不理解这块代码的话,也没有关系,后文会逐步讲解 Promise 的执行机制
function request(url,data = {}){ return new Promise((resolve,reject)=>{ $.ajax({ url, data, success:function (data) { resolve(data); }, error:function (error) { reject(error); } }) }); } 复制代码
用 request 方法实现前面多个互相依赖的网络请求
let url1 = 'http://xxx.xxx.1'; let url2 = 'http://xxx.xxx.2'; let url3 = 'http://xxx.xxx.3'; request(url1) .then((data)=>{ console.log(data); return request(url2,data) }) .then((data)=>{ console.log(data); return request(url3,data) }) .then((data)=>{ console.log(data) }) .catch((error)=>{ console.log(error); }); 复制代码
Promise 的特性
promise 的状态
- pending (等待态)
- fulfilled (完成态)
- rejected (拒绝态)
终值与拒因
- 终值:指的是 promise 被解决时传递给解决回调的值
- 拒因:拒绝原因,指在 promise 被拒绝时传递给异常回调的拒绝原因
状态与状态关系,状态与终值和拒因的关系
- pending 可以迁移至 fulfilled 或 rejected
- fulfilled 不能迁移至其他状态,必须拥有一个不可变的终值
- rejected 不能迁移至其他状态,必须拥有一个不可变的据因
Promise 的使用
构造函数
Promise 是一个构造函数,使用 new 操作符返回一个 promise 对象
构造函数接收一个 excutor 函数作为参数
excutor 函数有两个函数类型的参数 resolve 和 reject
let p = new Promise((resolve,reject)=>{ // 在 excutor 函数中执行异步操作 // 当异步操作完成后,调用 resolve 函数或 reject 函数 }); 复制代码
- 构造函数在调用时,excutor 函数会作为同步代码立即执行
- 我们通常在 excutor 函数中执行我们的异步操作
- 未调用 resolve、reject 函数时,promise 对象的状态为 pending
let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('p1'); },1000); }); // p1 的状态一直为 pending 复制代码
-
当调用 resolve 函数, resolve 的参数为非 promise 对象、非 thenable 对象
- resolve 函数的参数,作为 promise 对象的终值
- promise 对象的状态变为 fulfilled
let p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('p2'); resolve('我是p2的终值') },1000); }); // 代码执行,1000ms内,p2 的状态为 pending // 代码执行,1000ms后,p2 的状态为 fulfilled // 代码执行,1000ms后,p2 的终值为 '我是p2的终值' 复制代码
-
当调用 resolve 函数, resolve 的参数为 promise 对象
- promise 对象的状态、终值、拒因与传入的 promise 对象同步
let p = new Promise((resolve,reject)=>{ reject('error') }) let p1 = new Promise((resolve,reject)=>{ resolve(p) }) // p1 的状态为 rejected ,拒因为 error 复制代码
-
当调用 resolve 函数, resolve 的参数为 thenable 对象
- 会对 thenable 对象进行展开操作,promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果
let thenable1 = { then:function(resolve,reject){ resolve(1) } } let thenable2 = { then:function(resolve,reject){ reject(2) } } let thenable3 = { then:function(resolve,reject){ throw new Error(3) } } let thenable4 = { then:function(fn1,fn2){ //不调用 fn1 fn2 } } let p1 = new Promise((resolve,reject)=>{ resolve(thenable1); }) let p2 = new Promise((resolve,reject)=>{ resolve(thenable2); }) let p3 = new Promise((resolve,reject)=>{ resolve(thenable3); }) let p4 = new Promise((resolve,reject)=>{ resolve(thenable4); }) // p1 的状态为 fulfilled 终值为 1 // p2 的状态为 rejected 终值为 2 // p3 的状态为 rejected 拒因为 Error:3 // p4 的状态为 pending 复制代码
- 当调用 reject 函数, reject 函数的参数,作为 promise 对象的拒因
- promise 对象的状态变为 rejected
let p3 = new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('p3'); reject('我是p3的拒因') },1000); }); // 代码执行,1000ms内,p3 的状态为 pending // 代码执行,1000ms后,p3 的状态为 rejected // 代码执行,1000ms后,p3 的拒因为 '我是p3的拒因' 复制代码
promise对象上的方法
then方法:
promise 提供一个 then 方法,用于访问其终值和拒因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected); 复制代码
- onFulfilled 函数用于当 promise 状态变为 fulfilled 时,接收终值
- onRejected 函数用于当 promise 状态变为 rejected 时,接收拒因
new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('异步任务获取的数据') },50) }).then((data)=>{ console.log(data) }) // 异步任务获取的数据 复制代码
new Promise((resolve,reject)=>{ setTimeout(()=>{ reject(new Error('异步任务异常')) },50) }).then(null,(error)=>{ console.log(error) }) // Error: 异步任务异常 复制代码
new Promise((resolve,reject)=>{ throw new Error('抛出一个异常'); }).then(null,(error)=>{ console.log(error) }) // Error: 抛出一个异常 复制代码
onFulfilled 和 onRejected 参数可选
- 如果 onFulfilled 不是函数,其必须被忽略
- 如果 onRejected 不是函数,其必须被忽略
onFulfilled 特性
如果 onFulfilled 是函数:
- 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值
- 在 promise 执行结束前其不可被调用
- 其调用次数不可超过一次
onRejected 特性
如果 onRejected 是函数:
- 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因
- 在 promise 被拒绝执行前其不可被调用
- 其调用次数不可超过一次
onFulfilled 和 onRejected 的调用时机
- 当 promise 对象的状态变为 fulfilled 或 rejected 时调用
- onFulfilled、onRejected 永远都是异步调用
- onFulfilled、onRejected 在事件队列中作为微任务来处理
console.log(1); setTimeout(function(){ console.log(2) },0) new Promise((resolve,reject)=>{ resolve(3); }).then((data)=>{ console.log(data); }) console.log(4) // print: 1 4 3 2 复制代码
onFulfilled 和 onRejected 的调用要求
- onFulfilled 和 onRejected 必须被作为函数调用
- 非严格模式下,this 为全局对象
- 严格模式下,this 为 undefined
function fn1(){ new Promise((resolve)=>{ resolve(); }).then(function(){ console.log(this) }) } function fn2(){ "use strict"; new Promise((resolve)=>{ resolve(); }).then(function(){ console.log(this) }) } fn1(); // print: window fn2(); // print: undefined 复制代码
then方法的多次调用
- then 方法可以被同一个 promise 对象多次调用
- then 方法会返回一个新的 promise 对象
- 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
- 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调
let p = new Promise((resolve)=>{ resolve() }); let p1 = p.then(()=>{ console.log('异步执行,第一个onFulfilled'); }); let p2 = p.then(()=>{ console.log('异步执行,第二个onFulfilled'); }); console.log(p1.constructor === Promise); console.log(p === p1); console.log(p === p2); console.log(p1 === p2); // print: true // print: false // print: false // print: false // print: 异步执行,第一个onFulfilled // print: 异步执行,第二个onFulfilled 复制代码
then方法的返回值
then 方法返回一个 promise 对象
promise2 = promise1.then(onFulfilled, onRejected); 复制代码
- 若 onFulfilled 、onRejected 返回一个非promise对象、非thenable对象的值 x ,则 promise2 的状态为 fulfilled ,终值为 x
let p = new Promise((resolve,reject)=>{ throw new Error(); }); let p1 = p.then(null,(data)=>{ return '我是p2的终值' }); p1.then((data)=>{ console.log(data) }); // print: 我是p2的终值 复制代码
- 若 onFulfilled 、onRejected 返回一个 promise 对象的值 x ,promise2 的状态、终值、拒因与 x 同步
let p1 = new Promise((resolve,reject)=>{ resolve(1) }) let p2 = new Promise((resolve,reject)=>{ reject(2) }) let p3 = new Promise((resolve)=>{ resolve() }) let p4 = p3.then(()=>{ return p1; }) let p5 = p3.then(()=>{ return p2; }) // p4 的状态为 fulfilled 终值为 1 // p5 的状态为 rejected 拒因为 2 复制代码
- 若 onFulfilled 、onRejected 返回一个 thenable 对象 ,会对 thenable 对象进行展开操作,promise2 的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果
let thenable1 = { then:function(resolve,reject){ resolve(1) } } let thenable2 = { then:function(resolve,reject){ reject(2) } } let p1 = new Promise((resolve,reject)=>{ resolve() }) let p2 = p1.then(()=>{ return thenable1; }) let p3 = p1.then(()=>{ return thenable2; }) // p2 的状态为 fulfilled 终值为 1 // p3 的状态为 rejected 拒因为 2 复制代码
- 若 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 的状态为 rejected,拒因为 e
let p = new Promise((resolve,reject)=>{ resolve(); }); let p1 = p.then((data)=>{ throw new Error('error') }); p1.then(null,(err)=>{ console.log(err); }); // print: Error: error 复制代码
- 若 onFulfilled 不是函数且 promise1 成功执行, promise2 的状态为 fulfilled 终值为 promise1 的终值
let p = new Promise((resolve,reject)=>{ resolve('我是p1的终值'); }); let p1 = p.then(null,null); p1.then((data)=>{ console.log(data); }); // print: 我是p1的终值 复制代码
- 若 onRejected 不是函数且 promise1 拒绝执行, promise2 的状态为 rejected 拒因为 promise1 的拒因
let p = new Promise((resolve,reject)=>{ reject('我是p1的拒因'); }); let p1 = p.then(null,null); p1.then(null,(err)=>{ console.log(err); }); // print:我是p1的拒因 复制代码
- 若 onFulfilled、onRejected 执行过程中抛出异常,则 promise2 的状态为 rejected 拒因为抛出的异常
let p = new Promise((resolve,reject)=>{ resolve('我是p的终值'); }); let p1 = p.then((data)=>{ throw new Error('异常') }); p1.then(null,(err)=>{ console.log(err); }); // print:Error: 异常 复制代码
终值和拒因的穿透特性
-
如果 promise 的状态变为 fulfilled,then 方法没有注册 onFulfilled
- then 方法返回的 promise 对象的状态变为 fulfilled
- then 方法返回的 promise 对象的终值与原 promise 对象的终值相同
-
如果 promise 的状态变为 rejected,then 方法没有注册 onRejected
- then 方法返回的 promise 对象的状态变为 rejected
- then 方法返回的 promise 对象的拒因与原 promise 对象的拒因相同
let p1 = new Promise((resolve,reject)=>{ resolve(1) }) let p2 = new Promise((resolve,reject)=>{ reject(2) }) let p3 = p1.then(null,null); let p4 = p2.then(null,null); // p3 的状态是 fulfilled 终值 1 // p4 的状态是 rejected 拒因 2 p5 = p3.then(null,null); p6 = p4.then(null,null); // p3 的状态是 fulfilled 终值 1 // p4 的状态是 rejected 拒因 2 复制代码
- 穿透特性主要用于异常处理
let fn1 = function(){} let fn2 = function(){} let fn3 = function(){} let fn4 = function(){} let fn5 = function(){} let onError = function(){}; new Promise((resolve,reject)=>{ setTimeout(function(){ reject() }) }) .then(fn1) .then(fn2) .then(fn3) .then(fn4) .then(fn5) .then(null,onError) 复制代码
fn1、fn2、fn3、fn4、fn5 都可能发生错误,通过在最后的then函数注册的 onRejected 函数接收可能发生异常错误
catch方法:
catch(fn) 方法实际上是 then(null,fn) 方法的别名,catch 方法的返回值以及 catch 方法中出现异常的情况与调用 then 方法相同
new Promise((resolve,reject)=>{ reject() }).then(null,function(error){ }) // 等同于 new Promise((resolve,reject)=>{ reject() }).catch(function(error){ }) 复制代码
Promise 的静态方法
Promise.resolve
-
Promise.resolve 方法用于将现有数据转换为 promise 对象
-
若入参为 promise 对象
- 返回的 promise 对象的状态、终值、拒因与 Promise.resolve 方法的入参同步
-
若入参为 thenable 对象
- 会对 thenable 对象进行展开操作,返回的 promise 对象的状态、终值、拒因取决于 thenable 对象的 then 方法调用结果
-
若入参为非 promise 非 thenable 对象
- 返回的 promise 对象的状态为 fulfilled
- 返回的 promise 对象的终值为 Promise.resolve 方法的入参
-
若入参为 promise 对象
let p = Promise.resolve(x) // 等价于 let p = new Promise((resolve)=>{ resolve(x) }) 复制代码
Promise.reject
- Promise.reject 方法用于返回一个状态为 rejected ,拒因为方法入参的 promise 对象
let p = Promise.reject(x) // 等价于 let p = new Promise((resolve,reject)=>{ reject(x) }) 复制代码
Promise.all
- Promise.all 方法用于将多个 promise 对象包装成一个新的 promise 对象
const p = Promise.all([p1, p2, p3]); 复制代码
- p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象
-
p 的状态由 p1、p2、p3 决定
-
当 p1、p2、p3 的状态都变成 fulfilled
- p 的状态为 fulfilled
- 此时 p1、p2、p3 的终值组成一个数组,这个数组作为 p 的终值
-
当 p1、p2、p3 的状态有一个变成 rejected
- p 的状态变为 rejected
- 此时第一个状态变为 rejected 的 promise 对象的拒因作为 p 的拒因
-
当 p1、p2、p3 的状态都变成 fulfilled
let p1 = Promise.resolve(1); let p2 = Promise.resolve(2); let p3 = 3; Promise.all([p1,p2,p3]).then((data)=>{ console.log(data); // print: [1,2,3] }) 复制代码
let p1 = Promise.resolve(1); let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p2 error') },1000) }) let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p3 error') },500) }) Promise.all([p1,p2,p3]).catch((error)=>{ console.log(error); // print: p3 error }) 复制代码
Promise.race
- Promise.all 方法同样用于将多个 promise 对象包装成一个新的 promise 对象
const p = Promise.race([p1, p2, p3]); 复制代码
- p1、p2、p3 都是 promise 对象,如果不是,调用 Promise.resolve 方法转换为 promise 对象
- p 的状态由 p1、p2、p3 中状态最先变为 fulfilled 或 rejected 的 promise 对象决定
- p 的终值或拒因由最先变更状态的 promise 对象所决定
let p1 = Promise.resolve(1); let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p2 error') },1000) }) let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p3 error') },500) }) Promise.race([p1,p2,p3]).then(data=>{ console.log(data); }).catch(error=>{ console.log(error); }) // print: 1 复制代码
let p1 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve(1) },1000) }) let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p2 error') },800) }) let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('p3 error') },500) }) Promise.race([p1,p2,p3]).then(data=>{ console.log(data); }).catch(error=>{ console.log(error); }) // print: p3 error 复制代码
Promise 的错误捕获
当 promise 的状态为 rejected 且为对 promise 对象使用 catch 方法,此时的异常信息会被 promise 对象吃掉 可以通过监听 unhandledRejection 事件,专门监听未捕获的reject错误
// node 环境下 process.on('unhandledRejection', error => { console.log('unhandledRejection', error); }); // 浏览器下 window.addEventListener('unhandledrejection',(e)=>{ e.preventDefault(); console.log(e); }); 复制代码
Promise 的问题
- 无法取消Promise,若没有状态变更,也无法停止 promise 的等待
- 不设定 then 或 catch 方法,构造函数(excutor函数)错误,无法捕获
- 未完成状态时,无法得知是刚开始,还是即将完成
Promise 题目
题目一
const promise = new Promise((resolve, reject) => { console.log(1) resolve() console.log(2) }) promise.then(() => { console.log(3) }) console.log(4) 复制代码
结果:1 2 4 3
题目二
const promise = new Promise((resolve, reject) => { resolve('success1') reject('error') resolve('success2') }) promise .then((data) => { console.log(data) }) .catch((err) => { console.log(err) }) 复制代码
结果:success1
题目三
Promise.resolve(1) .then((data) => { console.log(data) return 2 }) .catch((err) => { return 3 }) .then((data) => { console.log(data) }) 复制代码
结果:1 2
题目四
Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log) 复制代码
结果:1
题目五
new Promise((resolve,reject)=>{ console.log(3); let p = new Promise((resolve, reject)=>{ console.log(7); setTimeout(()=>{ console.log(5); resolve(6); },0) resolve(1); }); resolve(2); p.then((arg)=>{ console.log(arg); }); }).then((arg)=>{ console.log(arg); }); console.log(4); 复制代码
结果:3 7 4 1 2 5
以上题目,如有疑问,可在留言区与我互动询问
预计每周1-2篇文章,持续更新,欢迎各位同学点赞+关注
后续内容参见 写作计划
写作不易,如果觉得稍有收获,欢迎~点赞~关注~
本文同步首发与 github ,欢迎在 issues 与我互动,欢迎Watch & Star ★
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 照片整理系列二 —— 照片整理及归档的辛酸历程
- 我自己整理的码农周刊一周分享整理
- 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)
- Hibernate 关系映射整理
- 大数据框架整理
- 树莓派资源整理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
赢在设计
[美] 洛芙迪 (Lance Loveday)、[美] 尼豪斯 (Sandra Niehaus) / 刘淼、枊靖、王卓昊 / 人民邮电出版社 / 2010-8 / 55.00
企业总是面临在网站设计和改进方面进行投资的抉择。怎样才能让有限的资金发挥出最大的效益呢?网站设计不应只是把网站做得赏心悦目,它更应该是提高经济收益和获得竞争优势的战略利器。是时候让网站发挥其潜能,以业务指标为导向来做设计决策,为提升网站收益而设计了。 作者凭借多年为众多网站做咨询工作的经验,为我们揭示了赢在设计的奥秘。它针对目前网站设计中存在的典型问题,先从宏观上探讨解决问题的战略手段,围绕......一起来看看 《赢在设计》 这本书的介绍吧!