Promise
对象代表一个异步操作, 有三种状态: pending
(进行中)、fulfilled
(已成功)和 rejected
(已失败)。
Promise
构造函数接受一个函数作为参数, 该函数的两个参数分别是 resolve
和 reject
。
const promise = new Promise(function (resolve, reject) {
// ... some code
// 异步操作成功
if (flag) {
resolve(value);
} else {
reject(error);
}
});
一般情况下都会使用 new Promise()
来创建 promise
对象, 但是也可以使用 promise.resolve
和 promise.reject
这两个方法:
Promise.resolve(value)
的返回值也是一个 promise
对象, 可以对返回值进行 .then
调用, 代码如下:
Promise.resolve(11).then(function (value) {
console.log(value); // 打印出11
});
resolve(11)
代码中, 会让 promise
对象进入确定(resolve
状态), 并将参数 11
传递给后面的 then
所指定的 onFulfilled
函数。
创建 promise
对象可以使用 new Promise
的形式创建对象, 也可以使用 Promise.resolve(value)
的形式创建 promise
对象。
Promise.reject
也是 new Promise
的快捷形式, 也创建一个 promise
对象。代码如下:
Promise.reject(new Error('我错了, 请原谅俺!!'));
就是下面的代码 new Promise
的简单形式:
new Promise(function (resolve, reject) {
reject(new Error('我错了!'));
});
下面是使用 resolve
方法和 reject
方法:
function testPromise(ready) {
return new Promise(function (resolve, reject) {
if (ready) {
resolve("hello world");
} else {
reject("No thanks");
}
});
};
// 方法调用
testPromise(true).then(function (msg) {
console.log(msg);
}, function (error) {
console.log(error);
});
上面的代码的含义是给 testPromise
方法传递一个参数, 返回一个 promise
对象, 如果为 true
的话, 那么调用 promise
对象中的 resolve()
方法, 并且把其中的参数传递给后面的 then
第一个函数内, 因此打印出 hello world
, 如果为 false
的话, 会调用 promise
对象中的 reject()
方法, 则会进入 then
的第二个函数内, 会打印 No thanks
。
Promise
有五个常用的方法: then()
、catch()
、all()
、race()
、finally
。下面就来看一下这些方法。
当 Promise
执行的内容符合成功条件时, 调用 resolve
函数, 失败就调用 reject
函数。Promise
创建完了, 那该如何调用呢?
promise.then(function (value) {
// success
}, function (error) {
// failure
});
then
方法可以接受两个回调函数作为参数。第一个回调函数是 Promise
对象的状态变为 resolved
时调用, 第二个回调函数是 Promise
对象的状态变为 rejected
时调用。其中第二个参数可以省略。then
方法返回的是一个新的 Promise
实例(不是原来那个 Promise
实例)。因此可以采用链式写法, 即 then
方法后面再调用另一个 then
方法。
当要写有顺序的异步事件时, 需要串行时, 可以这样写:
let promise = new Promise((resolve, reject) => {
ajax('first').success(function (res) {
resolve(res);
})
})
promise.then(res => {
return new Promise((resovle, reject) => {
ajax('second').success(function (res) {
resolve(res)
})
})
}).then(res => {
return new Promise((resovle, reject) => {
ajax('second').success(function (res) {
resolve(res)
})
})
}).then(res => {
})
那当要写的事件没有顺序或者关系时, 还如何写呢?可以使用 all
方法来解决。
Promise
对象除了有 then
方法, 还有一个 catch
方法, 该方法相当于 then
方法的第二个参数, 指向 reject
的回调函数。不过 catch
方法还有一个作用, 就是在执行 resolve
回调函数时, 如果出现错误, 抛出异常, 不会停止运行, 而是进入 catch
方法中。
p.then((data) => {
console.log('resolved', data);
}, (err) => {
console.log('rejected', err);
});
p.then((data) => {
console.log('resolved', data);
}).catch((err) => {
console.log('rejected', err);
});
all
方法可以完成并行任务, 它接收一个数组, 数组的每一项都是一个 promise
对象。当数组中所有的 promise
的状态都达到 resolved
的时候, all
方法的状态就会变成 resolved
, 如果有一个状态变成了 rejected
, 那么 all
方法的状态就会变成 rejected
。
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 2000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000)
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 3000)
});
Promise.all([promise1, promise2, promise3]).then(res => {
console.log(res);
//结果为: [1,2,3]
})
调用 all
方法时的结果成功的时候是回调函数的参数也是一个数组, 这个数组按顺序保存着每一个 promise
对象 resolve
执行时的值。
race
方法和 all
一样, 接受的参数是一个每项都是 promise
的数组, 但是与 all
不同的是, 当最先执行完的事件执行完之后, 就直接返回该 promise
对象的值。如果第一个 promise
对象状态变成 resolved
, 那自身的状态变成了 resolved
;反之第一个 promise
变成 rejected
, 那自身状态就会变成 rejected
。
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(1);
}, 2000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000)
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 3000)
});
Promise.race([promise1, promise2, promise3]).then(res => {
console.log(res);
//结果: 2
}, rej => {
console.log(rej)
})
那么 race
方法有什么实际作用呢?当要做一件事, 超过多长时间就不做了, 可以用这个方法来解决:
Promise.race([promise1, timeOutPromise(5000)]).then(res => {})
finally
方法用于指定不管 Promise
对象最后状态如何, 都会执行的操作。该方法是 ES2018
引入标准的。
promise
.then(result => {
// ···
})
.catch(error => {
// ···
})
.finally(() => {
// ···
});
上面代码中, 不管 promise
最后的状态, 在执行完 then
或 catch
指定的回调函数以后, 都会执行 finally
方法指定的回调函数。
下面是一个例子, 服务器使用 Promise
处理请求, 然后使用 finally
方法关掉服务器。
server.listen(port)
.then(function () {
// ...
})
.finally(server.stop);
finally
方法的回调函数不接受任何参数, 这意味着没有办法知道, 前面的 Promise
状态到底是 fulfilled
还是 rejected
。这表明, finally
方法里面的操作, 应该是与状态无关的, 不依赖于 Promise
的执行结果。finally
本质上是 then
方法的特例:
promise.finally(() => {
// 语句
});
// 等同于
promise.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
上面代码中, 如果不使用 finally
方法, 同样的语句需要为成功和失败两种情况各写一次。有了 finally
方法, 则只需要写一次。