Promise 是异步编程的一种解决方案优于传统的解决方案——回调函数和事件。 简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。 Promise对象表明一个异步操做,有三种状态:
pending
(进行中)、resolved
(已成功)和rejected
(已失败)。es6
setTimeout(() => {
console.log('a');
}, 0);
let p = new Promise((resolve, reject) => {
console.log('b');
resolve();
});
p.then(() => {
console.log('d');
});
console.log('c');
// 控制台输出:
// 'b'
// 'c'
// 'd'
// 'a'
复制代码
要理解该输出顺序首先应该了解js的执行任务队列优先级(由高到低)编程
首先setTimeout
属于宏任务扔进Macro-tasks队列,新建实例Promise
时接受一个回调函数做为参数,注意此时该回调函数属于主线程会马上执行,输出'b'
紧接着执行resolve
也就意味着该promise
对象的状态将从pending
更新为resolved
,其挂载的回调函数也就是then里面的参数函数并不会当即执行,由于它属于微任务,因此丢进Micro-task队列。接下来输出'c'
,到目前为止主线程任务已经结束,接着执行微任务输出'd'
,最后执行宏任务输出'a'
。数组
let p1 = new Promise(function (resolve, reject) {
resolve('p1');
});
let p2 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('p2')
}, 100);
});
let p3 = new Promise(function (resolve, reject) {
setTimeout(() => {
reject('p3')
resolve('p3')
}, 100);
});
p1.then((value) => {
console.log(value);
})
p2.then((value) => {
console.log(value);
})
p3.then((value) => {
console.log('success', value);
}, (value) => {
console.log('error', value);
})
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);
setTimeout(() => {
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);
}, 100);
// 控制台输出
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[pending]]}
// p3: Promise {[[pending]]}
// p1
// p2
// error p3
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[resolved]]: "p2"}
// p3: Promise {[[rejected]]: "p3"}
复制代码
p1最新建立就调用了resolve
则它的状态马上变为resolved
,值为p1,但此时p2和p3都为pending
状态,100毫秒后p2输出值p2且状态转为resolved
。 p3首先调用了reject
则其状态转为rejected
,值为p3,尽管下一行又调用了resolve但并无任何做用忽略成功的回调,只有error p3
。 这段实验也显示出Promise的一个特色promise
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('a');
}, 1000);
}).then(function (value) {
console.log("第一个" + value);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value + 'b');
}, 1000);
})
}).then(function (value) {
console.log("第二个" + value);
}).then(function (value) {
console.log("第三个" + value);
console.log(a);
}).then(function (value) {
console.log("第四个" + value);
}, (err) => {
console.log("第四个error", err);
})
// 第一个a
// 第二个ab
// 第三个undefined
// 第四个error ReferenceError: a is not defined
复制代码
then
方法是Promise的实例方法,调用then
后的返回值依然是一个promise对象,注意它是全新的promise对象,通常能够看到then
的链式调用,这里须要注意区别于jQuery的链式调用。jQuery是返回调用对象自己。当链式调用时要注意不能被它绕晕了,要抓住一个重点,咱们只是在调用then
方法而已,给它传参只是定义函数,并无执行!何时执行?是根据你的异步操做后的promise状态如何更新以及什么时候更新而肯定。 传给then
的回调函数中的返回值影响着最终返回出的promise对象,参数的返回值通常有三种状况。dom
undefined
,固然它也属于普通同步值。则then
最终返回的是状态是resolve
成功的Promise对象,如上段代码的第三个输出,它的前一个then方法内部没有返回值则默认undefined
,接下来就直接走进第三个then
方法,且值value
就是undefined
。then
方法将根据这个Promise的状态和值建立一个新的Promise对象返回。如第二个输出,会等待上个then方法返回的新Promise对象状态的更新来肯定,且会等待它的更新以及将最后的值传过来,这种状况也是当有多级异步操做所使用的方式。throw
一个同步异常,then
方法将返回一个rejected
状态的Promise, 值是该异常。如第四个输出!Promise.prototype.catch方法是then(null, rejection)的别名,用于指定发生错误时的回调函数。异步
let p = new Promise((resolve, reject) => {
//
});
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
复制代码
catch方法,它首先是捕捉处理错误,不管是promise调用了reject方法仍是直接抛出错误,都会走到catch方法内进行处理。接下来就和then方法同样,返回的也是一个全新的Promise对象,错误处理的回调函数返回值一样有三种状况,具体看上个then方法。异步编程
let p = new Promise((resolve, reject) => {
reject('失败')
});
p.then((val) => console.log('1then: success', val))
.then((val) => console.log('2then: success', val))
.catch((val) => console.log('3catch: error', val))
.catch((val) => console.log('4catch: error', val))
.then((val) => console.log('5then: success', val))
// 控制台输出
// 3catch: error 失败
// 5then: success undefined
复制代码
Promise 对象的错误具备“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误老是会被下一个catch语句捕获。 上段代码首先p
这个Promise对象(状态是resolved)遇到第一个then
会忽略掉它定义的成功回调,注意此时调用完第一个then
方法后的返回值是全新的Promise对象!且状态一样是resolved
,为什么会这样?由于它把p
的状态进行了一层包装也就做为了本身的状态,且值也和它同样!因此说Promise
的状态具备传递性。函数
由于这个错误目前并无被捕获处理,因此继续向后传递。一样遇到第二个then
时咱们能够当作跳过它,但发生的细节和第一个then
同理,直到3catch
将这个错误捕获,因此输出3catch: error 失败
。上面也提到catch
也就是then
的一个别名而已,本质其实差很少。故此时catch
调用后的返回值再次是一个全新的promise对象,那状态呢?由于这边给catch
传递的参数并无定义返回值,因此默认就是一个同步值undefined
,则catch
返回的promise对象的状态就是resolved
。那么它调用最后一个then
输出5then: success undefined
,也就不难理解了。post
let p1 = Promise.resolve('p1');
p1.then(val => console.log('success', val), val => console.log('error', val))
let p2 = Promise.reject('p2');
p2.then(val => console.log('success', val), val => console.log('error', val))
复制代码
当传入参数是通常同步值时则返回一个状态为resolve或reject的Promise对象,值也就是传入的参数,相应的会调用成功或失败的回调。ui
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(p1);
let p3 = new Promise(function (resolve, reject) {
resolve(p1);
});
console.log(p1 === p2)
console.log(p1 === p3)
p1.then((value) => { console.log('p1=' + value)})
p2.then((value) => { console.log('p2=' + value)})
p3.then((value) => { console.log('p3=' + value)})
// 控制台输出:
// true
// false
// p1=1
// p2=1
// p3=1
复制代码
当传入一个Promise对象时,则resolve
就直接返回该Promise对象,故p1 === p2
为true
,p3则为全新的Promise对象,可是它状态马上变为resolve
且值为p1,它会获取p1的状态和值做为本身的值。故p3=1
。
function timeout(who) {
return new Promise(function (resolve, reject) {
let wait = Math.ceil(Math.random() * 3) * 1000;
setTimeout(function () {
if (Math.random() > 0.5) {
resolve(who + ' inner success');
}
else {
reject(who + ' inner error');
}
}, wait);
console.log(who, 'wait:', wait);
});
}
let p1 = timeout('p1');
let p2 = timeout('p2');
p1.then((success) => { console.log(success) }).catch((error) => { console.log(error) })
p2.then((success) => { console.log(success) }).catch((error) => { console.log(error) })
// race只要有一个状态改变那就当即触发且决定总体状态失败仍是成功.
// all只要有一个失败那就当即触发总体失败了,两个都成功总体才成功.
Promise.all([p1, p2])
.then((...args) => {
console.log('all success', args)
})
.catch((...args) => {
console.log('someone error', args)
})
// 控制台输出(状况1)
// p1 wait: 3000
// p2 wait: 1000p2 inner error
// someone error [ 'p2 inner error' ]
// p1 inner success
// 控制台输出(状况2)
// p1 wait: 2000
// p2 wait: 2000
// p1 inner success
// p2 inner success
// all success [ [ 'p1 inner success', 'p2 inner success' ] ]
复制代码
all、race方法接受数组做为参数,且数组每一个成员都为Promise对象。若是不是的话就调用Promise.resolve方法,将其转为 Promise 实例,再进一步处理。使用表示要包装的多个promise异步操做来肯定。具体能够看代码理解,要多动手本身试验!
若有错误或疑问欢迎指正留言: