[ES6] Promise.then()使用小结
promise简介
- 为了解决回调地狱嵌套,在EcmaScript6中新增了一个API:Promise。Promise是一个构造函数。Promise是一个容器,Promise本身不是异步的,但是容器内部往往存放一个异步操作。
- Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。resolve和reject两个形参接收的是函数,接受的函数由 JavaScript 引擎提供,不用自己部署。
- 通过new Promise构造器创建的promise对象是有状态的,状态可能是pending(进行中),resolved(已完成,也称为fullfilled),rejected(已失败)中的一种。新创建的promise对象的状态是pending,后续可以变为resolved或rejected,状态一旦改为resolved或rejected,就不会再变了。
- resolve函数调用后会将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
- reject函数调用后将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
例一:
const promise = new Promise(function(resolve, reject) { // resolve 和reject这两个形参是由 JavaScript 引擎传进来的,是两个函数
// 这里进行一些异步操作代码
if (/* 异步操作成功 */){
resolve(value); // resolve函数的作用是将这个promise对象的状态从 pending 变为 resolved。并将异步操作的结果value,作为参数传递出去。
console.log(1)
} else /* 异步操作失败 */{
reject(error); // reject函数的作用是将这个promise对象的状态从 pending 变为 rejected。并将错误传递出去。
}
});
- Promise实例生成以后,可以用then方法指定两个回调函数。
- then方法中的第一个参数,是promise对象的状态由pending变为resolved后会调用的回调函数;then方法中的第二个参数,是promise对象的状态由pending变为rejected后调用的回调函数;
- 这两个回调函数中,rejectedCallback可选的,不一定要提供。
- 例一中,如果异步操作成功,就会调用resolve(value),调用resolve(value),当前promise的状态由进行中变为成功,随即调用then方法中的第一个回调函数参数。
- 我们在例一中调用resolve(value)的时候其实就可以看作是在调用then方法里传的第一个回调函数。
- then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行( 也就是说resolve()后面如果还有其他同步语句(如上面例子中的console.log(1) ),会先执行完这些同步语句, 然后在执行then方法中的回调函数)。(其实这里涉及到的是事件循环的概念,不清楚的可以去了解)
promise.then(resolvedCallback, rejectedCallback);
promise.then(function(value) {
// 把这个函数作为promise对象状态变为成功后调用的回调函数
// 成功时的逻辑
}, function(error) {
// 把这个函数作为promise对象状态变为失败后调用的回调函数
// 失败时的逻辑
});
例二:
- 下面的代码中,首先利用Promise构造器创建了一个promise对象,并且调用了then方法指定了该promise对象的状态由pending变为resolved后会调用的回调函数。
- promise里的异步操作是一个定时操作,当计时到了后,调用了resolve()函数(也就是调用了我们在then方法中写的第一个回调函数),该函数调用后,promise对象的状态由pending变为resolved。
new Promise((resolve, reject) => {
setTimeout(function () {
resolve() ;
}, 1000)
}).then((data) => { // 因为new Promise返回的就是一个promise对象实例,所以这里可以链式操作
alert('成功的回调');
})
promise容器中的执行顺序
new Promise((resolve, reject) => {
setTimeout(function () {
console.log('时间到了')
resolve('11')
console.log('22')
}, 1000)
console.log('33')
}).then((data) => {
console.log(data)
})
console.log('hhh')
// 打印顺序:
// 33
// hhh
// 时间到了
// 22
// 11
- promise本身不是异步的,只是内部包含了异步代码。
- 首先启动定时器(但是不会执行定时器内部的回调函数)
- 因为setTimeout是一个异步代码,不会影响后续代码的继续执行,所以随即执行 console.log(‘33’)
- new Promise部分就执行完了,然后按顺序执行 console.log(‘hhh’)
- 当定时到了后,会执行定时器中的回调函数,依次执行 console.log(‘时间到了’) resolve(‘11’) (实际上可以看作执行then方法传过去的回调函数) console.log(‘22’)
- 按照上面的说法应该先打印11,在打印22呀?这是为什么呢?
- 这就是前面提到的 then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行, 也就是说console.log(22)会先打印。
resolve函数和reject函数中传递的参数
- 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例。
如果resolve或reject中的参数是一个正常的值
例三:
// 1. 创建Promise实例对象
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
// 调用resolve函数后p1的状态pending->fulfilled
// 然后就会调用成功的回调函数,并将这个参数1传递给回调函数
resolve(1);
}, 1000);
})
// 2. 通过then方法指定成功和失败时的回调函数
p1
.then(function (data) {
// 成功的回调函数 (在定时器中调用resolve函数后 就会调用当前这个回调函数)
console.log("data", data)
}, function (err) {
// 失败的回调
console.log("err", err)
})
// 打印顺序
// 1s后打印'data 1'
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
reject(1); // 调用reject函数后,p1的状态pending->rejected
}, 1000);
})
p1
.then(function (data) {
console.log("data", data)
}, function (err) {
console.log("err", err)
})
// 打印顺序
// 1s后打印'err 1'
如果resolve或reject中的参数是promise实例对象
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('1');
}, 3000);
})
var p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(p1); // resolve的参数是一个promise对象
}, 1000);
})
p2
.then(function (data) {
console.log('resolve执行啦')
console.log(data)
}, function (err) {
console.log(err)
})
// 打印顺序
// 3s后依次打印 'resolve执行啦' '1'
- 上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数。
- 注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
Promise.prototype.then()
- then方法会返回一个新的Promise实例。因此可以采用链式写法,即then方法后面再调用另一个then方法。采用链式的then,可以指定一组按照次序调用的回调函数。
成功时的回调 和 失败时的回调函数 中的return返回值
- 第一个回调函数完成以后,会将返回结果作为参数,传入下一级的回调函数。返回结果可能是一个参数或者一个新的promise实例(即有异步操作),这时候一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
- 如果then方法中的回调函数(不管是成功的回调还是失败的回调)返回了一个参数(return xxx),那么这个then方法返回的新的promise的状态会变成fulfilled(同时成功的回调函数也会被调用)。
如果返回值为一个普通参数
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
// 执行resolve函数后p1的状态 pending->fulfilled
// 然后执行p1成功时的回调函数 并传递参数
resolve('1');
}, 1000);
})
var p2 = p1.then(function (data) { // p1成时的回调函数
console.log("data", data);
return 'hello p2'; // 执行return语句后p2的状态pending->fulfilled 同时p2成功的回调函数会被调用
// 当前函数中 return 的结果就可以在后面的 then 中 function 接收到
}, function (err) { // p1失败时的回调函数
console.log("err", err);
return 'hello p2 nihao';
}); // 当前then返回一个新的promise:p2
var p3 = p2.then(function (data) { // p2成功时的回调函数
console.log("data", data);
}, function (err) { // p2失败时的回调函数
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'data hello p2'
写成链式调用的形式:
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('1');
}, 1000);
})
p1
.then(function (data) {
console.log("data", data);
return 'hello p2';
}, function (err) {
console.log("err", err)
return 'hello p2 nihao';
})
.then(function (data) {
console.log("data", data);
}, function (err) {
console.log("err", err);
})
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
reject('1'); // 执行resolve函数后p1的状态pending->rejected 失败的回调
}, 1000);
})
var p2 = p1.then(function (data) {
console.log("data", data);
return 'hello p2';
}, function (err) {
console.log("err", err)
return 'hello p2 nihao'; // 执行return语句后p2的状态pending->fulfilled
// 可以看出不管是成功的回调还是失败的回调中的return 但会导致当前then方法返回的promise变为成功状态
}); // 当前then返回一个新的promise:p2
p2.then(function (data) {
console.log("data", data);// 这个回调会被调用
}, function (err) {
console.log("err", err);
})
// 打印顺序
// 1s后打印'err 1'
// 紧接着打印'data hello p2 nihao'
- 从上面的例子可以总结出:如果then方法中返回一个参数(return xxx),那么这个then方法返回的新的promise的状态会变成fulfilled。
如果返回值为一个新的promise实例
var time1, time2
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
// 1s后进入这里
// 执行resolve函数后p1的状态 pending->fulfilled
// 然后执行p1成功时的回调函数 并传递参数
resolve('1');
}, 1000);
})
var p2;
p1.then (function (data) { // p1成功时的回调函数
time1 = Date.now()
console.log("data", data);
p2 =
new Promise(function (resolve, reject) {
setTimeout(function () {
// 执行resolve函数后p2的状态 pending->fulfilled
// 然后执行p2成功时的回调函数 并传递参数
resolve('2');
}, 3000);
});
// return 一个 Promise 对象的时候,当前then()方法的返回值就是这个p2对象
return p2;
}, function (err) { // p1失败时的回调函数
console.log("err", err)
})
.then (function (data) { // p2成功时的回调函数
time2 = Date.now()
console.log(time2-time1)
console.log("data", data);
}, function (err) { // p2失败时的回调函数
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 再过3s后依次打印'3001' 和 'data 2'
写成链式的
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('1');
}, 1000);
})
p1.then (function (data) {
console.log("data", data);
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('2');
}, 3000);
});
}, function (err) {
console.log("err", err)
})
.then (function (data) {
console.log("data", data);
}, function (err) {
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 再过3s后依次打印'3001' 和 'data 2'
成功时的回调 和 失败时的回调函数 中抛出异常
- 如果then中抛出异常,那么这个then返回的新的promise实例的状态会被置为rejected
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
// p1状态pending->fulfilled
// 调用p1成功时的回调函数
resolve('1');
}, 1000);
})
var p2 = p1.then (function (data) { // p1成功时的回调
console.log("data", data);
throw new Error('test1'); // 抛出异常后p2状态pending->rejected
}, function (err) { // p1失败时的回调
console.log("err", err);
throw new Error('test2');
})
p2.then (function (data) { // p2成功时的回调
console.log("data", data);
}, function (err) { // p2失败时的回调
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err Error: test1'
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
// p1状态pending->rejected
// 调用p1失败时的回调函数
reject('1');
}, 1000);
})
var p2 = p1.then (function (data) { // p1成功时的回调
console.log("data", data);
throw new Error('test1');
}, function (err) { // p1失败时的回调
console.log("err", err);
throw new Error('test2'); // 抛出异常后p2状态pending->rejected
})
p2.then (function (data) {
console.log("data", data);
}, function (err) { // p2状态变为失败后调用这个回调
console.log("err", err);
})
// 打印顺序
// 1s后打印'err 1'
// 紧接着打印'err Error: test2'
Promise.prototype.catch()
- Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('1');// 1.调用resolve后p1的状态pending->fulfilled
}, 1000);
})
var p2 = p1.then (function (data) { // p1状态变为成功后调用这个回调
console.log("data", data);
throw new Error('test1'); // 抛出异常后p2状态pending->rejected
}, function (err) {
console.log("err", err);
throw new Error('test2');
})
p2.catch (function (err) { // p2状态变为失败后调用这个回调
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err Error: test1'
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('1');// 1.调用resolve后p1的状态pending->fulfilled
}, 1000);
})
p1.then (function (data) { // p1状态变为成功后调用这个回调
console.log("data", data);
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject('2'); // 2.调用reject后p2的状态pendging->rejected
}, 3000);
});
}, function (err) {
console.log("err", err)
})
.catch (function (err) { // p2状态变为失败后调用这个回调
console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err 2'
参考文章
https://es6.ruanyifeng.com/#docs/promise
https://segmentfault.com/a/1190000011652907
文章来源于互联网:[ES6] Promise.then()使用小结