前言
这一章说一下ES6的Promise对象。为什么要在PWA系列的文章中讲Promise呢?因为PWA中的许多技术API中都是以Promise返回的方式返回的,为了对后续章节中PWA技术API更好的理解,这里就来说一个Promise对象。
Promise出现的背景
在JavaScript当中,处理异步操作时,我们需要知道操作是否已经完成,当执行完成的时候会返回一个回调函数,表示操作已经完成。所以在处理异步操作时,通常是使用回调嵌套的方式(CallBack)。但是如果出现多层回调嵌套,也就是我们常说的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验。
像这样:
function a1() {function a2() {function a3() {function a4() {function a5() {...}}}}
}
回调方式主要会导致两个关键问题:
- 嵌套太深代码可读性太差
- 行逻辑必须串行执行。
在这种情况下,Promise 对象出现了。2015 年 6 月,加入了ECMAScript 6 的标准。
Promise简介
Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。一个 Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。它允许你以一种同步的方式编写异步代码。Promises 将嵌套的回调改造成一系列的.then
的链式调用,去除了层层嵌套的劣式代码风格。Promises 不是一种解决具体问题的算法,而已一种更好的代码组织模式。
上面的代码,用Promise的方式可以写成:
a1.then(function(data) {return a2(data)
})
.then(function(data) {return a3(data)
})
.then(function(data) {return a4(data)
})
.then(function(data) {return a5(data)
})
...
Promise 对象有以下两个特点:
- 对象的状态不受外界影响。 Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)、 Rejected(已失败)。根据异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示无法通过其他手段改变。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。
Promise状态:
语法
new Promise( function(resolve, reject) {...} /* executor */ );
executor
是一个带有 resolve
和 reject
两个参数的函数 。executor
函数在Promise构造函数执行时同步执行,被传递 resolve
和 reject
函数(executor
函数在Promise构造函数返回新建对象前被调用)。resolve
和 reject
函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor
内部通常会执行一些异步操作,一旦完成,可以调用resolve
函数来将promise状态改成fulfilled,或者在发生错误时将它的状态改为rejected。
如果在executor
函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
基本用法
new Promise(function(resolve, reject) {// ... some codeif (/* 操作成功 */){resolve(value);} else {reject(error);}
});
注意:实例化的Promise对象会立即执行
方法
下面说一下Promise对象的方法。
Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。then
方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then
方法返回的是一个新的Promise实例。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
Promise.prototype.catch()
catch()
方法返回一个Promise,只处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected)
相同。用于指定发生错误时的回调函数。
taskkA()
.then(function() {return taskB()
})
.then(function() {return taskC()
})
.catch(function(err) {// ...
})
.then(function() {return taskD()
})
Promise.resolve()
返回一个状态由给定value决定的Promise对象。如果该值是一个Promise对象,则直接返回该对象;如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。
Promise.resolve(value)
Promise.resolve(promise)
Promise.resolve(thenable)
有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
Promise.resolve('data')
// 等价于
new Promise(resolve => resolve('data'))
Promise.reject()
返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法。该实例的状态为rejected。
var p = Promise.reject('出错了');
// 等同于
var p = new Promise((resolve, reject) => reject('出错了'))p.catch(function(err) {console.log(err)
})
// 出错了
Promise.all()
这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。
Promise.all(iterable)
iterable: 一个可迭代对象,例如一个 Array 或 String。
上述可迭代对象中的所有 Promise 被 resolve 之后返回 resolve,或者在任一 Promise 被 reject 后返回 reject。
Promise.race()
当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
用法和Promise.all()类似。
Promise.race(iterable);
基本应用
下面根据Promise的特性,做几个例子。
异步加载图片
function loadImage(url) {return new Promise(function(resolve, reject) {var img= new Image();img.onload = function() {resolve(img);};img.onerror = function(err) {reject(new Error(err));};img.src = url;});
}
网络请求超时处理
Promise.race([fetch('http://xxxx.xxx'),new Promise(function (resolve, reject) {setTimeout(() => reject(new Error('请求超时')), 6000)})
]).
then(function(data) {// ...
})
.catch(function(err) {// 处理错误...
})
总结
这一篇里,对Promise的背景由来,及相关方法进行了相应的介绍说明,也了解到了Promise在异步处理上的使用优势。
博客名称:王乐平博客
CSDN博客地址:http://blog.csdn.net/lecepin