🛞 Promise 链式调用
🌰 例子 / Promise 链式调用:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000)
}).then(result => result * 2)
.then(result => result * 2)
.then(result => console.log(result)) // 4
2
3
4
5
Promise 返回的结果通过
.then
处理程序链进行传递。只所以可以传递是因为每个对.then
的调用都会返回一个新的 Promise 结果。
不会传递 Promise 的结果的情况(分开 .then
):
点击查看
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});
promise.then(function(result) {
alert(result); // 1
return result * 2;
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里相当于是 平行的处理程序,彼此独立运行处理任务。这种属于极少情况。
# 在链式调用中返回 Promise
处理程序可以创建并返回一个 Promise,这样的情况下,其他的处理程序需要等待它的 状态确定(settled)获得其结果。
🌰 例子:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) {
cosole.log(result) // 4
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这个 Promise 的链式调用中有三个
.then
。第一个.then
返回一个新的 Promise 一秒之后会进行resolve
,然后结果(result)
回传递给第二个.then
。第二个.then
的动作与第一个.then
相同。
返回 Promise 可以构建异步行为链。
🌰 例子 / 加载脚本 的例子:
loadScript("/article/promise-chaining/one.js")
.then(function(script) {
return loadScript("/article/promise-chaining/two.js");
})
.then(function(script) {
return loadScript("/article/promise-chaining/three.js");
})
.then(function(script) {
// 使用在脚本中声明的函数
// 以证明脚本确实被加载完成了
one();
two();
three();
});
2
3
4
5
6
7
8
9
10
11
12
13
14
使用箭头函数重写:
loadScript("/article/promise-chaining/one.js")
.then(script => loadScript("/article/promise-chaining/two.js"))
.then(script => loadScript("/article/promise-chaining/three.js"))
.then(script => {
// 脚本加载完成,我们可以在这儿使用脚本中声明的函数
one();
two();
three();
});
2
3
4
5
6
7
8
9
提示
准确的说,处理程序 返回的并不是一个 Promise,而是一个 「thenable」 对象,一个具有方法 .then
的任意对象,它可以当做是一个 Promise 来对待。
可以自定义实现「Promise 兼容对象」:
点击查看
可以具有拓展的方法集,但也与原生的 Promise 兼容,因为它们实现了 .then
方法。
🌰 例子:
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve); // function() { native code }
// 1 秒后使用 this.num*2 进行 resolve
setTimeout(() => resolve(this.num * 2), 1000); // (**)
}
}
new Promise(resolve => resolve(1))
.then(result => {
return new Thenable(result); // (*)
})
.then(alert); // 1000ms 后显示 2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 链式调用实例: fetch
在前端中,通常使用 Promise 处理网络请求。
🌰 例子 / 向 user.json
发送请求,并从服务器加载该文本:
let promise = fetch(url)
fetch('/article/promise-chaining/user.json')
.then((response) => response.text())
.then((text) => console.log(text))
2
3
当全部文字内容从远程服务器下载完成后,它会返回一个 Promise,该 Promise 以刚刚下载完成的这个文本作为
result
进行resolve
。
可以将 response
的远程内容解析为 JSON:
fetch('/article/promise-chaining/user.json')
.then(response => response.json())
.then(user => alert(user.name));
2
3
🌰 例子 / 上面的例子上,再向 GitHub 发送一个请求,加载用户个人资料并显示头像:
fetch('/article/promise-chaining/user.json')
// 将其加载为 JSON
.then(response => response.json())
// 发送一个到 GitHub 的请求
.then(user => fetch(`https://api.github.com/users/${user.name}`))
// 将响应加载为 JSON
.then(response => response.json())
// 显示头像图片(githubUser.avatar_url)3 秒(也可以加上动画效果)
.then(githubUser => {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
setTimeout(() => img.remove(), 3000); // (*)
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
为了使得链可拓展,需要返回一个在头像显示结束时进行 resolve
的 Promise:
fetch('/article/promise-chaining/user.json')
.then(response => response.json())
.then(user => fetch(`https://api.github.com/users/${user.name}`))
.then(response => response.json())
.then(githubUser => new Promise(function(resolve, reject) { // (*)
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
setTimeout(() => {
img.remove();
resolve(githubUser); // (**)
}, 3000);
}))
// 3 秒后触发
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
所以,为了能让其中的请求能服用(可控制),每次的异步行为都应该返回一个 Promise,可以使得后续的行为成为可能。
最后,将代码拆分为可重用的函数:
function loadJson(url) {
return fetch(url)
.then(response => response.json());
}
function loadGithubUser(name) {
return loadJson(`https://api.github.com/users/${name}`);
}
function showAvatar(githubUser) {
return new Promise(function(resolve, reject) {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
使用例子:
loadJson('/article/promise-chaining/user.json')
.then(user => loadGithubUser(user.name))
.then(showAvatar)
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
2
3
4
# 总结
.then
(或者.catch
/.finally
处理程序)会返回一个 Promise 的结果(value 或者 error),可以进一步传递。- 也可以返回一个 新的 Promise,要等待它的 状态确定,其结果才能进一步传递。
# 实例
# 区分不同的 Promise 调用
promise .then(f1) .catch(f2);
1
2
3与
promise .then(f1, f2);
1
2
点击查看
这两种代码片段行为不相同。第一个中 f1
出现错误时,会被 catch(f2)
处理。但是在第二个中,不会被处理,因为 f1
下面没有处理来自 f1
的 Promise 结果(或者错误)的链。