昨天讲了关于 Promise 的理解,有人在下面评论说要我出关于源码的解析,能力有限只能循序渐进了,我们还是先把基础的搞明白,再逐步深入。
Promise对象充当执行者(“产生代码”或“singer”)和消费函数(“fans”)之间的链接,它们将接收结果或错误。使用.then、.catch和.finally方法可以注册(订阅)消费函数。
最重要,最基本的是 then 。
他的的语法是:
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
then的第一个参数是一个函数,它在promise被解析时运行,并接收结果。
then的第二个参数是一个函数,当promise被拒绝时运行,并接收错误。
例如,以下是人们对成功解决的反应:
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
// resolve runs the first function in .then
promise.then(
result => alert(result), // shows "done!" after 1 second
error => alert(error) // doesn't run
);
执行了第一个函数。
在被拒绝的情况下,第二个
let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// reject runs the second function in .then
promise.then(
result => alert(result), // doesn't run
error => alert(error) // shows "Error: Whoops!" after 1 second
);
如果我们只对成功完成感兴趣,那么我们只能为.then提供一个函数参数:
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // shows "done!" after 1 second
如果我们只对错误感兴趣,那么可以使用null作为第一个参数:.then(null, errorHandlingFunction)。或者我们可以使用.catch(errorHandlingFunction),两者完全相同:
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) is the same as promise.then(null, f)
promise.catch(alert); // shows "Error: Whoops!" after 1 second
调用.catch(f)完全是对.then(null, f)的模拟,它只是一个简写。
就像在一个普通的try{…} catch{…}终于有了承诺。
调用.finally(f)类似于.then(f, f),因为f总是在promise被解决时运行:无论是resolve还是reject。
finally是一个很好的处理程序,用于执行清理,例如停止我们的加载指示器,因为它们不再需要了,无论结果是什么。
是这样的:
new Promise((resolve, reject) => {
/* do something that takes time, and then call resolve/reject */
})
// runs when the promise is settled, doesn't matter successfully or not
.finally(() => stop loading indicator)
// so the loading indicator is always stopped before we process the result/error
.then(result => show result, err => show error)
也就是说,finally(f)并不是then(f,f)的别名。有一些细微的区别:
finally处理程序没有参数。在最后,我们不知道诺言是否成功。没关系,因为我们的任务通常是执行“一般的”收尾过程。
finally处理程序将结果和错误传递给下一个处理程序。
例如,这里的结果被传递给finally:
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
})
.finally(() => alert("Promise ready"))
.then(result => alert(result)); // <-- .then handles the result
在promise中有一个错误,最后传递给catch:
new Promise((resolve, reject) => {
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err)); // <-- .catch handles the error object
我们在前一章中使用了loadScript函数来加载脚本。
下面是基于回调的变体,只是为了提醒我们:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
我们用 Promise 重写一下。
新的函数loadScript不需要回调。相反,它将创建并返回一个Promise对象,该对象将在加载完成时解析。外部代码可以使用.then向其添加处理程序(订阅函数):
function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(script);
});
}
用法
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('Another handler...'));