看完下面文章,你将了解到:
1、什么是Promise?
2、Promise怎样使用以及使用的场景,解决了什么问题
3、ES7较ES6又增加了哪些支撑Promise的语法糖
复制代码
一、Promise
Promise顾名思义,字面理解是"承诺",那承诺什么呢?下面我们就来探究下.
首先,我们先看看Promise是怎么使用的,然后再试着去理解它.
该新特性属于 ECMAScript 2015(ES6)规范.
Promise.prototype 属性表示 Promise构造器的原型.它允许你可以在构造器的原型对象添加属性或方法到所有 Promise 实例上.
复制代码
我们先来看下,Promise.prototype是什么东西?在浏览器打印看下,下图:
可以看到Promise原型上,默认定义了catch,finally,then方法和一个返回的构造函数constructor.现在我们就来一一举个例子:
Promise.prototype.constructor();//彩蛋放后
Promise.prototype.catch(onRejected)
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.finally(onFinally)
复制代码
1、Promise.prototype.catch(onRejected) 该方法返回一个Promise函数,回调函数添加一个拒绝(rejection),该回调函数处理拒绝的情况.等价与Promise.prototype.then(undefined, onRejected)
感觉还是一脸懵逼是吧,来看下例子: 便于理解,我这里参数就随便定义了success,fail
let promise = new Promise(function(success,fail){
throw 'fail';
});
promise.catch(function(resouce){
console.dir('then resouce==>'+resouce);
});
等价与Promise.prototype.then(undefined, onRejected)
复制代码
2、Promise.prototype.then(onFulfilled, onRejected) 该方法返回一个Promise函数,回调函数添加一个then,该回调函数最多可添加两个参数,返回成功信息和失败的状态信息.
let promise = new Promise(function(success,fail){
success('have successed');
});
promise.then(function(data){
console.dir('then data==>'+data);
},function(err_reso){
console.dir('then reso==>'+err_reso);
})
复制代码
等价于
let promise = new Promise(function(success,fail){
success('have successed');
});
promise.then((data)=>{
console.dir('then data==>'+data);
}
).catch((resorce)=>{
console.dir('then resorce==>'+resorce);
});
复制代码
到这里,你应该看明白了吧,为了更专业性,下面我的写法会有所变化了...
3、Promise.prototype.finally(onFinally) 该方法补充了then和catch方法---执行catch,then之后都会执行的事件处理回调.避免在then和catch重复的代码.
let promise = new Promise(function(resove,reject){
resove('have successed');
});
promise.then((data)=>{
console.dir('then data==>'+data);
}
).catch((e)=>{
console.dir('then resorce==>'+e);
}).finnaly(()=>{
// all loading
console.dir('all loading');
});
复制代码
二、Promise之链式调用
看了上面的例子,好像Promise类似于Ajax的用法,又像一个代理. 没错Promise还真就做了这些事情.文章一开头说的,Promise为承诺而生.它承诺你在某个时候或某个状态执行给予反馈结果.
由于JavaScript的代码都是单线程的,导致所有网络操作,浏览器事件都必须是异步操作.通常的异步执行有三种,回调函数、事件监听以及发布订阅.我们这里只谈回调函数
1、先看个例子:
function foo(){
console.log('1秒后执行');
}
let fn=setTimeout(function(){
foo();
},1000);
//1秒后执行
复制代码
上面是个典型的回调函数的实现.
2、再来看下下面的例子:
function foo(){
console.log('1秒后执行');
}
function fo(){
console.log('1秒后执行');
}
setTimeout(function(){
fo();
setTimeout(function(){
foo();
},1000);
},1000);
复制代码
如果有n个需求呢,是不是一直这么定义下去.看上去是不是很乱,不美观难以维护呢?
Promise首先最直观的的解决了,这个问题,让代码变得更为简洁,它采用链式的方式
3、对上面的例子做已调整,如下
function fo(){
console.log('1秒后执行');
}
let promise =new Promise(function(resove,reject){
setTimeout(function(){
return resove(fo);
},1000);
});
promise.then((data)=>{
setTimeout(function(){
return resove(data);
},1000);
}).then((data)=>{
console.log(data);
});
复制代码
以上代码是不是整洁了很多,有人会质疑promise不可能就这点能耐吧!
不着急,我们先来看下链式的原理,更进一步的了解下,然后再探讨其它的用法:
Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象
一个 Promise有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。
我们用视图来梳理下:
复制代码
pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。
出自 Promise.then() – JavaScript | MDN 通过上面介绍,相信对Promise的优点有了一定的了解了, 下面就常用的场景,再补充一下:4、图片加载为例:
"en">
"UTF-8">
"viewport" content="width=device-width, initial-scale=1.0">
"X-UA-Compatible" content="ie=edge">
Promise
"wrap">
"loading">正在加载...
"pics">
复制代码
function loadImg (url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
resolve(img)
}
img.onerror = reject
img.src = url
})
}
复制代码
//递归调用
function syncLoad (index) {
if (index >= urls.length) return Promise.resolve()
return loadImg(urls[index])
.then(img => {
// addToHtml(img)
return syncLoad (index + 1)
})
}
// 调用
syncLoad(0)
.then(() => {
document.querySelector('.loading').style.display = 'none'
})
.catch(console.log)
复制代码
介绍完了链式处理Promise对象的实例之后,我们再来研究下.并发请求...
三、并发请求(Promise.all)
对于不需要按顺序加载,只需要按顺序来处理的并发请求,Promise.all 是最好的解决办法。因为Promise.all 是原生函数,我们就引用文档来解释一下。
Promise.all(iterable) 方法指当所有在可迭代参数中的 promises 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。 出自 Promise.all() – JavaScript | MDN
const promises = urls.map(loadImg)
Promise.all(promises)
.then(imgs => {
imgs.forEach(addToHtml)
document.querySelector('.loading').style.display = 'none'
})
.catch(err => {
console.error(err, 'Promise.all 当其中一个出现错误,就会reject。')
})
复制代码
并发请求,按顺序处理结果
Promise.all 虽然能并发多个请求,但是一旦其中某一个 promise 出错,整个 promise 会被 reject.所以我们只要知道哪些图片出错了,把出错的图片再做一次请求或着用占位图补上就好.然后按顺序处理结果
方法一:
let task = Promise.resolve()
for (let i = 0; i < promises.length; i++) {
task = task.then(() => promises[i]).then(addToHtml)
}
复制代码
方法二:
promises.reduce((task, imgPromise) => {
return task.then(() => imgPromise).then(addToHtml)
}, Promise.resolve())
复制代码
四、并发请求(Promise.race)
Promise.race 接受一个 Promise 数组,返回这个数组中最先被 resolve 的 Promise 的返回值。
Promise.all接受一个 Promise 数组,返回这个数组中最后被 resolve 的 Promise 的返回值。
复制代码
通俗一点的理解:
all方法的效果实际上是【谁跑的慢,以谁为准执行回调】
race方法的效果实际上是【谁跑的快,以谁为准执行回调】
复制代码
因与all用法相似,这里就不做过多赘述了,下面我们来看下,ES7较ES6又增加了哪些支撑Promise的语法糖...
五、ES7支撑Promise的语法糖
老规矩,下看下实例:
function successCallback(result) {
console.log("It succeeded with " + result);
}
function failureCallback(error) {
console.log("It failed with " + error);
}
doSomething(successCallback, failureCallback);
复制代码
async function foo() {
try {
let result = await doSomething();
let newResult = await doSomethingElse(result);
let finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
复制代码
上面的例子,是否发现async、await这样的字眼,不见了then,catch了,对这就是在ECMAScript 2017标准的async/await语法糖中,这种同步形式代码的整齐性得到了极致的体现. 以上代码等价与
function foo() {
doSomething()
.then(result => doSomethingElse(value))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
}
复制代码
好处不言而喻
最后总结一下:
Promise是ES6的新特性,
简化异步请求层层回调的操作,
all和race实现并发请求,为很多业务场景提供了最佳的解决方案.
以及ES7新增的语法糖async,await大大简化了,Promise复杂的处理逻辑.
以上为个人一点点见解,如有错误或疑问,欢迎留言批评指正,如果没有设计到的请大家提供一下新的方式和方法。
复制代码
参考资料 JavaScript Promise:简介 | Web | Google Developers JavaScript Promise迷你书(中文版) JavaScript 之MDN web docs