本文将从浅到深的去剖析promise。因为内容较多,分为上下两篇。javascript
js是一门单线程的语言,因此其中会涉及到不少异步的操做,异步编程的解决方案有不少种,咱们主要讲一种最基础的和这篇文章的主角(promise):java
在异步编程中,这种的应用范围最广,举个定时器的例子:编程
setTimeout(() => {
// ...
}, 1000);
复制代码
固然在业务中异步请求也会用到不少,但当多个异步操做须要串行操做的时候,就会有回调地狱产生。promise
$.get(url, data1 => {
console.log(data1)
$.get(data1.url, data2 => {
console.log(data2)
$.get(data2.url, data3 => {
console.log(data3)
})
})
})
复制代码
代码没有美感,且不利于维护。固然,咱们能够经过减小代码嵌套,模块化等手段来修复。可是并不以下面的解决方案优雅。 bash
const reqMethod = (url) => {
return new Promise((reslove, reject) => {
$.get(url, data => {
if(data.success) {
reslove();
} else {
reject();
}
})
})
}
reqMethod(url).then((data1) => {
return reqMethod(data1.url);
}).then((data2) => {
return reqMethod(data2.url);
}).then((data3) => {
return reqMethod(data3.url);
})
复制代码
这样的实现方式,符合易于阅读,由于每一步操做都是按照前后顺序进行的。异步
Promise从不一样角度理解,有不少种含义。模块化
promise从字面意思理解,就是许诺和承诺的意思,对于一种承诺而言,有三种状态:异步编程
一、承诺还未达成,还在纠结过程当中(pending状态)
二、承诺没有实现,失言了(rejected状态)
三、承诺实现了,就是成功的状态(fulfilled状态)
复制代码
在这里不过多展开,你们能够去看Promise/A+ 规范或者ECMAscript规范;函数
Promise是一个对象,是一个构造函数,ES6将其写进了语言标准。统一了用法。最基础的用法以下:ui
const promiseMethod = new Promise((resolve, reject) => {
// some code
if(/*异步成功的条件*/) {
resolve();
} else {
reject();
}
})
复制代码
Promise是一个构造函数,最基础的做用就是用new操做符生成一个实例对象
const promiseMethod = new Promise((resolve, reject) => {
// some code
if(/*异步成功的条件*/) {
resolve();
} else {
reject();
}
})
复制代码
Promise
可接受的参数是一个函数,resolve
和reject
是该函数的两个参数,由js引擎提供,不须要本身定义。
resolve
的做用是将pending
状态变动为fulfilled
状态。
reject
的做用是将pending
状态变动为rejected
状态。
Promise新建时就会当即执行,与什么时候调用无关,与结果也无关
举个例子
const promiseOne = new Promise((resolve, reject) => {
console.log('has finish');
resolve();
})
复制代码
一、console.log
在新建过程当中就执行了。
二、promiseOne
的结果已经固定下来了,不管什么时候调用,结果都不会发生改变。
then
方法是被定义在Promise
的原型上,做用是:添加Promise
状态改变后的回调函数。
then
方法接收两个参数,第一个参数是resolve
状态执行的函数,第二个参数是reject
执行的函数。
then
方法返回的是一个新的Promise
对象。
举个例子:
const promiseOne = new Promise((resolve, reject) => {
console.log('has finish');
resolve();
})
const promiseTwo = new Promise((resolve, reject) => {
console.log('has reject');
reject();
})
const promiseThree = promiseOne.then(() => {
console.log('成功的回调')
}, () => {
console.log('失败的回调')
})
promiseTwo.then(() => {
console.log('成功的回调')
}, () => {
console.log('失败的回调')
})
console.log(promiseThree);
复制代码
输出的结果:
has finish
has reject
<Promise>(pending)
成功的回调
失败的回调
复制代码
catch
方法其实和then
第二个回调函数的别名,做用是用于储物发生时的回调处理。
catch
捕获两类错误:
一、异步操做时抛出错误,致使状态变为reject
二、then
回调过程当中产生的错误。
举个例子:
//第一种状况:异步操做过程当中报错
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// 第二种状况:then执行过程当中报错
const promise = new Promise((resolve, reject) => {
resolve();
})
promise.then(() => {
throw new Error('test');
}).catch(function(error) {
console.log(error);
});
复制代码
关于catch
和then
第二个参数的区别:
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
复制代码
第二种写法的优势在于,then执行过程当中的报错,catch同样能捕获,优于第一种写法。
更深刻的错误捕获咱们单独放一章讲。
finally
方法做用在于无论promise
返回的状态是什么,它都会在执行。
finally
不接受任何参数。
promise
.then(function(data) {
// success
})
.catch(function(err) {
// error
}).finally(() => {
});
复制代码
finally
中执行的状态与promise
的结果无关,并且在方法中没法得知promise
的运行结果。
用法:
Promise.all([p1, p2, p3]);
复制代码
做用:是将多个promise
示例封装成一个promise
实例。结果只有如下两种情形:
全部promise
都成功,总的promise
才会成功
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
const p2 = new Promise((resolve, reject) => {
resolve('hello');
})
Promise.all([p1, p2]).then(() => {
console.log('所有成功')
}).catch(() => {
console.log('所有失败')
})
// 所有成功
复制代码
只要有一个promise
的状态从pending
变成reject
就是失败。
const p1 = new Promise((resolve, reject) => {
resolve();
})
const p2 = new Promise((resolve, reject) => {
reject();
})
Promise.all([p1, p2]).then(() => {
console.log('所有成功')
}).catch(() => {
console.log('所有失败')
})
复制代码
Promise的时间如何计算?
const newDate = new Date();
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 500)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000)
})
Promise.all([p1, p2]).then(() => {
const now = new Date();
let time = now.getTime() - newDate.getTime();
console.log(time);
})
// 1001
复制代码
按最长的那个为准。
用法:
Promise.race([p1, p2, p3]);
复制代码
做用:是将多个promise
示例封装成一个promise
实例。
Promise.race与Promise.all的区别
Promise.race
的状态取决于最早完成的promise
的状态。
举个例子,咱们须要对一个请求作5秒的timeout,就能够用Promise.race
。
const reqMethod = (url) => {
return new Promise((reslove, reject) => {
$.get(url, data => {
reslove();
})
})
}
const timeout = new Promise((resolve, reject) => {
setTimeout(() => {
reject('timeout')
}, 5000)
})
Promise.race([reqMethod(xxx),timeout]).then(() => {
console.log('请求成功')
}).catch(() => {
console.log('timeout')
})
复制代码
做用:须要将现有的对象转化为promise
对象。
Promise.resolve({});
//等价于
new Promise(resolve => resolve({}));
复制代码
Promise.resolve会根据传入的不一样参数作不一样的处理
不作任何操做,原封不动的返回该对象。
当即执行thenable
对象中的then
方法,而后返回一个resolved
状态的promise
。
let thenable = {
then: function(resolve, reject) {
resolve('success')
}
}
const p = Promise.resolve(thenable);
p.then((e) => {
console.log(e);
})
复制代码
直接返回一个resolved
状态的promise
。并将参数带给回调函数。
const p = Promise.resolve('参数');
p.then((e) => {
console.log(e)
})
复制代码
直接返回一个resolved
状态的promise
。
其做用是返回一个新的promise
实例,状态直接为reject
Promise.reject('error');
//等价于
new Promise((resolve, reject) => reject('error'));
复制代码
不一样于Promise.resolve
,其参数会原封不动的做为reject
的理由。
举个例子:
const p = Promise.reject('error');
p.catch((e) => {
console.log(e)
})
// error
复制代码
一、promise表明一个异步操做,一共有三种状态:pending、fulfilled和rejected。
二、promise的结果只服从于异步操做的结果,成功进入fulfilled状态,失败进入rejected状态。
复制代码
一、promise状态变化只有两种可能,一种从pending到fulfilled,或者是从pending到reject。
二、当状态变动完时,状态将不在发生改变。
复制代码
本文对promise的用法进行了详解,以后会更新两篇深刻一点的文章: