Promise

Promise类的基本用法如下,构造promise时,需传入一个高阶函数,其中resolve表示任务执行成功后的回调函数,reject表示任务执行失败后的错误处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let promise = new Promise(function(resolve, reject) {
setTimeOut(() => resolve("done!"),1000)
});

promise.then(result => {
console.log(result)
// done!
},err => {
console.log(err)
// 若出错,则打印错误信息
});

// 或者也可以catch
promise.catch(err => {
console.log(err)
})

实际上,resolve函数只是在promise的功能执行完毕后,把需要的信息传递出来,传递到”then”里处理,真正的回调函数主体是需要在then中详细写出的,resolve只是起到一个传递消息的功能。

Promise链

当有多个异步任务(如任务A、任务B、任务C)需要依次执行的时候,实现方法是在A的回调函数中执行B,在B的回调函数中执行C,为了避免写成回调函数嵌套形式,可以使用promise-then链的写法,好处是更加清晰,方便阅读和修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
new Promise(function(resolve, reject) {

setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

alert(result); // 1
return result * 2;

}).then(function(result) { // (***)

alert(result); // 2
return result * 2;

}).then(function(result) {

alert(result); // 4
return result * 2;

});

then函数传入了一个处理resolve结果的回调函数f(result),返回的是一个Promise类,这个promise类实际上是这样的:

1
2
3
4
5
// 伪代码,仅为了说明,无法实际执行
new Promise(function(resolve,reject) {
let new_result = f(result)
resolve(new_result) // 将上一个then对result的处理结果作为新的result通过resolve传到下一个then里
})

实际上就是将上一个then对result的处理结果作为新的result通过resolve传到下一个then里,这样就可以实现promise-then链了。

有一个特殊的情况,当then内的回调函数返回的是一个新的Promise类时,js会直接把这个类作为then的返回值,依次执行A、B、C,可以这样实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
new Promise(function(resolve, reject) {
// ... ... 执行任务A
resolve("A is done!")

}).then(result => {

alert(result); // A is done!

return new Promise((resolve, reject) => { // (*)
// ... ... 执行任务B
resolve("B is done!")
});

}).then(result => { // (**)

alert(result); // B is done!

return new Promise((resolve, reject) => {
// ... ... 执行任务C
resolve("C is done!")
});

}).then(result => {

alert(result); // C is done!
alert("A B C all done!")
}).catch(err => {
// ... 处理错误
})

当然,不手动创建新的Promise,直接在then里执行后续的任务也是可以的,像下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Make a request for user.json
fetch('/article/promise-chaining/user.json')
// Load it as json
.then(response => response.json())
// Make a request to github
.then(user => fetch(`https://api.github.com/users/${user.name}`))
// Load the response as json
.then(response => response.json())
// Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it)
.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); // (*)
})
.catch(err => {
// handle error ...
})

Promise.all

若有多个任务需要同时运行,且需要等待这些任务全都完成后执行后续操作,就需要用到Promise.all了,如下面这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];

// map every url to the promise fetch(github url)
let requests = urls.map(url => fetch(url));

// Promise.all waits until all jobs are resolved
let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
.then(responses => {
// all responses are ready, we can show HTTP status codes
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // shows 200 for every url
}

return responses;
})
// map array of responses into array of response.json() to read their content
.then(responses => Promise.all(responses.map(r => r.json())))
// all JSON answers are parsed: "users" is the array of them
.then(users => users.forEach(user => alert(user.name)));

Async/await

在自定义的函数前加上“async”关键字,可以让这个函数像then那样返回一个Promise类,Async函数体的编写要点和then是一模一样的。下面是例子:

1
2
3
4
5
async function f() {
return 1;
}

f().then(alert); // 1

await可以让当前程序的线程阻塞,等待指定的Promise执行结束,并得到resolve返回的结果,如:

1
2
3
4
5
6
7
8
9
10
11
12
async function f() {

let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});

let result = await promise; // wait till the promise resolves (*)

alert(result); // "done!"
}

f();