回调函数的返回值,指的是Promise实例的then的回调函数resolve和reject的返回值。
如代码:
let foo = new Promise((resolve, reject) => {
resolve("foo");
}).then(msg => {
console.log(msg);
msg += " return value";
return msg; //这一行代码即是返回值
})
初期接触Promise的人可能不会注意到,Promise实例是一个Promise对象,而他的then方法在执行后的返回值也是一个Promise对象,但这两个对象是并不是同一个Promise对象。
如代码:
let foo = new Promise((resolve, reject) => {});
let bar = foo.then();
console.log(foo === bar); //false
并且更有意思的是,由于then方法在执行后的返回值是一个Promise对象,因此他也可以有一个then方法(即then的连写),但这个Promise对象也和之前的Promise对象不是同一个Promise对象。
如代码:
let foo = new Promise((resolve, reject) => {
});
let bar = foo.then();
console.log(foo === bar); //false
let baz = bar.then();
console.log(foo === baz); //false
console.log(bar === baz); //false
1、基本应用:
then的连写的最基本的应用就是连写then,如以下代码:
new Promise((resolve, reject) => {
setTimeout(_ => {
reject("res");
}, 1000)
}).then(resVal => {
resVal += " barRes";
console.log(resVal);
return resVal;
}, rejVal => {
console.log(rejVal);
rejVal += " barRej";
return rejVal;
}).then(resVal => {
resVal += " bazRes";
console.log(resVal);
return resVal;
}, rejVal => {
console.log(rejVal);
rejVal += " bazRej";
return rejVal;
});
//输出结果
res
res barRej bazRes
连写then的特点如下(对照上面代码查看):
简单几个词总结一下:
2、当返回值是Promise实例时:
在上面,我们看到两个Promise实例交互时的情况,即一个Promise实例是另外一个Promise实例的参数。
这当遇见这种情况的时候有一个特点,只有作为参数的Promise实例的状态从pending转变为resolved或者rejected时,另一个Promise实例的回调函数才会执行。
而当then的回调函数的返回值是Promise实例时,那么由于这个Promise实例会作为下个then的参数,因此下个then会等待这个返回值的Promise实例的状态从pending发生改变后,才会继续执行。
如代码:
let foo = new Promise(function (res, rej) {
setTimeout(function () {
res("foo");
}, 1000)
})
foo.then(function (v) {
console.log(v);
return new Promise(function (res, rej) {
setTimeout(function () {
//这里执行的是reject
rej("1")
}, 1000)
});
}).then(undefined, function (val) {
//因此then这里执行的也是reject,而不是resolve
console.log(val);
})
//foo 第一个then的输出,延迟1秒
//1 第二个then的输出,再延迟1秒
Promise实例是有值的,但这个值不能直接获取,只能通过实例的回调函数的参数,或者通过控制台查看Promise实例时,通过[[PromiseValue]]
得知。
得到Promise的值的方法有以下一种方法:
设置Promise实例的值的方法有以下两种:
设置值的示例:
let foo = new Promise((res, rej) => {
setTimeout(function () {
res("foo");
}, 1000)
})
foo.then(val => {
console.log(val); // 显示当前Promise的对象的值
return "new value"; // 返回值将作为新的Promise实例的值
}).then(val => {
console.log(val); // 显示当前的值
})
// foo
// new value
值的变化:
在上面讨论两个Promise实例互动时,即将一个Promise实例作为值传递给了另外一个Promise的回调函数。
在这个传递过程中,Promise回调函数的实例的参数,就是Promise的值。
Promise作为返回值的示例:
let foo = new Promise((res, rej) => {
setTimeout(function () {
res("foo");
}, 1000)
})
foo.then(function (v) {
console.log(v);
return new Promise((res, rej) => { //这里相当于把Promise对象作为新的Promise对象(即第二个then的调用者)的值
setTimeout(() => {
//这里执行的是reject
rej("Promise return value")
}, 1000)
});
}).then(undefined, (val) => {
//因此then这里执行的也是reject,而不是resolve
console.log(val);
})
//foo 第一个then的输出
//Promise return value 第二个then的输出
简单来说,当执行Promise内部的代码时,如果抛错,那么将执行then的第二个回调函数(reject)。
或者使用catch替代then,然后执行第一个回调函数。
如代码:
let foo = new Promise((res, rej) => {
throw "This is a error"
})
foo.then(val => {
console.log(val) //不执行
}, err => {
console.log(err) //执行这行
})
foo.catch(err => {
console.log(err) //也执行这行
})
//This is a error
//This is a error
那么当抛出错误的时候,发生什么事情呢?
通过查看foo对象可以得知,当抛出错误后,foo这个Promise实例,会将[[PromiseStatus]]
状态设置为reject
(因此会执行reject回调函数),又会将[[PromiseValue]]
的值设置为抛出的错误信息。
因此,会执行then的reject的方法,并且reject的值是抛出的错误信息
而因为foo.catch(callback)
相当于 foo.then(null, reject)
,因此使用catch的时候相当于执行了then的reject回调函数,可以用来捕获错误信息。
脑洞时刻
1、假如抛出多个错误会发生什么事情呢?
哦,不会发生什么事情,后面的的抛错会被无视,如果抛错的后面有执行resolve或者reject,那么也会被无视(因为Promise的值只能被设置一次,设置之后就不可改变了)。
同样也因为这个原因,假如在抛错前就执行了resolve或者reject,那么抛错也会被无视。
2、catch里抛错呢?
由于then的返回值是一个新的Promise对象,因此catch里的抛错就会被新的Promise的reject所捕获,所以可以在catch后继续catch,从此子子孙孙无穷尽也。