Promise
- 生产者代码:会做一些事情,并且需要一些时间
- 消费者代码:想要在生产者代码完成工作的第一时间获得其工作成果。
- Promise是将生产者代码和消费者代码连接在一起的一个特殊的对象。在生产者准备好时,将结果向所有消费者开放
Promise对象的构造器语法
let promise = new Promise(function(resolve, reject) {
// executor(生产者代码)
});
- resolve(value) — 如果任务成功完成并带有结果 value。
- reject(error) — 如果出现了 error,error 即为 error 对象。
- executor 会自动运行并尝试执行一项工作。尝试结束后,如果成功则调用 resolve,如果出现 error 则调用 reject。
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
// 1 秒后发出工作已经被完成的信号,并带有结果 "done"(error)
setTimeout(() => resolve("done"), 1000);
//setTimeout(() => reject(new Error("Whoops!")), 1000);
});
- executor通过new Promise被自动调用
- executor 接受两个参数:resolve 和 reject。这些函数由 JavaScipt 引擎预先定义,因此我们不需要创建它们
- 经过1s的处理,executor 调用 resolve(“done”) 来产生结果。这将改变 promise 对象的状态
只能有一个结果或一个error
executor 只能调用一个 resolve 或一个 reject。所有其他的再对 resolve 和 reject 的调用都会被忽略:
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
以 Error 对象 reject
reject(new Error("..."))
中可以使用任何参数来完成,但建议使用Error
对象
Resolve/reject 可以立即进行
executor
通常是异步执行某些操作,并在一段时间后调用 resolve/reject
,但这不是必须的。我们还可以立即调用 resolve
或 reject
。
let promise = new Promise(function(resolve, reject) {
// 不花时间去做这项工作
resolve(123); // 立即给出结果:123
});
state 和 result 都是内部的
Promise
对象的 state
和 result
属性都是内部的。我们无法直接访问它们。但我们可以对它们使用 .then/.catch/.finally
方法。
消费者:then,catch,finally
- 消费函数接受
结果
或error
。可以通过使用.then、.catch 和 .finally
方法为消费函数进行注册。
then
语法:
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
- .then 的第一个参数是一个函数,该函数将在 promise resolved 后运行并
接收结果
。 - .then 的第二个参数也是一个函数,该函数将在 promise rejected 后运行并接收
error
。
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
//setTimeout(() => reject(new Error("Whoops!")), 1000);
});
promise.then(
result => alert(result), // 1 秒后显示 "done!"
error => alert(error) // 不运行
//result => alert(result), // 不运行
//error => alert(error) // 1 秒后显示 "Error: Whoops!"
);
如果我们只对成功完成的情况感兴趣,那么我们可以只为.then
提供一个函数参数
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // 1 秒后显示 "done!"
catch
如果我们只对error感兴趣:
- 我们可以使用null作为第一个参数
.then(null, errorHandlingFunction)
- 也可以使用
.catch(errorHandlingFunction)
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) 与 promise.then(null, f) 一样
promise.catch(alert); // 1 秒后显示 "Error: Whoops!"
finally
- promise中也有finally,finally总是在promise被settled时运行:即promise被resolve或reject。
- .finally(f) 调用与 .then(f, f) 类似
- promise是执行清理很好的处理程序
new Promise((resolve, reject) => {
/* 做一些需要时间的事儿,然后调用 resolve/reject */
})
// 在 promise 为 settled 时运行,无论成功与否
.finally(() => stop loading indicator)
// 所以,加载指示器(loading indicator)始终会在我们处理结果/错误之前停止
.then(result => show result, err => show error)
- finally 处理程序(handler)没有参数。在 finally 中,我们不知道 promise 是否成功。
- finally 处理程序将结果和 error 传递给下一个处理程序。
例如,在这儿结果被从 finally 传递给了 then:
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
})
.finally(() => alert("Promise ready"))
.then(result => alert(result)); // <-- .then 对结果进行处理
- 在这儿,promise 中有一个 error,这个 error 被从 finally 传递给了 catch:
new Promise((resolve, reject) => {
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err)); // <-- .catch 对 error 对象进行处理
- 因为 finally 并不是意味着要处理 promise 的结果。所以它将结果传递了下去。
- 如果 promise 为 pending 状态,.then/catch/finally 处理程序(handler)将等待它。否则,如果 promise 已经是 settled 状态,它们就会运行:
// 下面这 promise 在被创建后立即变为 resolved 状态
let promise = new Promise(resolve => resolve("done!"));
promise.then(alert); // done!(现在显示)
示例: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重写上面的代码:
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...'));