JavaScript-Promise
# JavaScript-Promise
Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。
Promise 是 ES6引入的异步编程的新解决方案。语法上 Promise 是一个构造函数用来封装异步操作并可以获取其成功或失败的结果。
- Promise 构造函数: Promise(excutor)
- Promise.prototype.then 方法
- Promise.prototype.catch 方法
# Promise基本语法
// 实例化 Promise对象
const p = new Promise(function(resolve,reject){
setTimeout(function(){
// 成功的数据
// let data = '用户数据'
// resolve(data)
// 失败的数据
let err = '数据读取失败'
reject(err)
},1000)
})
p.then(function(value){
console.log(value) // 用户数据
},function(reason){
console.log(reason) // 数据读取失败
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
需求:如果我想分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒:
setTimeout(function () {
console.log("First");
setTimeout(function () {
console.log("Second");
setTimeout(function () {
console.log("Third");
}, 3000);
}, 4000);
}, 1000);
// First
// Second
// Third
2
3
4
5
6
7
8
9
10
11
12
13
# 构造 Promise
现在我们用 Promise 来实现上面的功能:
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
// First
// Second
// Third
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Promise 的使用
Promise 构造函数只有一个参数,是一个函数
这个函数在构造之后会直接被异步运行,所以我们称之为起始函数
起始函数包含两个参数 resolve 和 reject
resolve 和 reject 都是函数,其中调用 resolve 代表一切正常,reject 是出现异常时所调用的:
new Promise(function (resolve, reject) {
console.log('run')
var a = 0;
var b = 1;
if (b === 0) reject("Divide zero");
else resolve(a / b);
}).then(function (value) {
console.log("a / b = " + value);
}).catch(function (err) {
console.log(err);
}).finally(function () {
console.log("End");
});
// run
// a / b = 0
// End
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Promise 常见问题
Q: 什么时候适合用 Promise 而不是传统回调函数?
A: 当需要多次顺序执行异步操作的时候,例如,如果想通过异步方法先后检测用户名和密码,需要先异步检测用户名,然后再异步检测密码的情况下就很适合 Promise。
Q: Promise 是一种将异步转换为同步的方法吗?
A: 完全不是。Promise 只不过是一种更良好的编程风格。
# Promise API
# Promise.all()
该方法的作用是将多个 promise 对象实例包装,生成并返回一个新的 promise 实例。返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
都成功-->成功
有一个失败-->失败
成功结果-->是几个promise对象成功结果组成的结果
失败结果-->有一个失败的promise结果的结果
只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
2
3
4
5
6
7
8
9
# Promise.race()
只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数
const p = Promise.race([p1, p2, p3]);
# Promise.allSettled()
有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。
Promise.all()
可以确定所有请求都成功了,但是只要有一个请求失败,它就会报错,而不管另外的请求是否结束
ES2020 (opens new window) 引入了Promise.allSettled()
方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做Settled
,包含了fulfilled
和rejected
两种情况
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
// 数组promises包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator()才会执行。
2
3
4
5
6
7
8
9
10
# Promise.any()
该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回
只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。
Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是Promise.any()
不会因为某个 Promise 变成rejected
状态而结束,必须等到所有参数 Promise 变成rejected
状态才会结束。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
console.log(first);
} catch (error) {
console.log(error);
}
2
3
4
5
6
7
8
9
10
11
12
Promise.any()
方法的参数数组包含三个 Promise 操作。其中只要有一个变成fulfilled
,Promise.any()
返回的 Promise 对象就变成fulfilled
。如果所有三个操作都变成rejected
,那么await
命令就会抛出错误
# Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数f
是同步函数还是异步操作,但是想用 Promise 来处理它。
因为这样就可以不管f
是否包含异步操作,都用then
方法指定下一步流程,用catch
方法处理f
抛出的错误。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
2
3
4
5
# async await
异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。
异步函数实际上原理与 Promise 原生 API 的机制是一模一样的,只不过更便于程序员阅读。
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}
// Promise 函数
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});
// async 函数
async function asyncFunc() {
await print(1000, "First");
await print(4000, "Second");
await print(3000, "Third");
}
asyncFunc();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23