Promise
Promise 对象表示异步操作最终的完成(或失败)以及其结果值。
Promise是一个构造函数,一个 Promise 是一个代理,它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。
一个 Promise 必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用,因此在异步操作完成和绑定处理程序之间不存在竞态条件,即使异步操作已经完成(成功或失败),在这之后通过 then() 添加的回调函数也会被调用。
如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。
使用 Promise
通常,Promise 的构造器接收一个执行函数 (executor),我们可以在这个执行函数里手动地 resolve 和 reject 一个 Promise。
const test = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成');
//resolve('随便什么数据');
reject(new Error('出错了'))
}, 2000);
});
test.then(function(result){
console.log(result)
}).catch(function(error){
console.log(error)
})
const test = new Promise((resolve, reject) => {
setTimeout(() => {
//resolve('-- 随便什么数据 --')
reject(new Error('出错了'))
}, 2000)
})
test.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
只是new了一个对象,并没有调用它,传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
//resolve('-- 随便什么数据 --')
reject(new Error('出错了'))
}, 2000)
})
return test
}
runAsync().then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
链式调用
连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现这种需求。
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync2(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据222 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync3(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据333 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
runAsync().then(result => {
console.log(result)
return runAsync2()
}).then(result => {
console.log(result)
return runAsync3()
}).then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync2(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
//resolve('-- 随便什么数据222 --')
reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync3(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据333 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
runAsync().then(result => {
console.log(result)
return runAsync2()
}).then(result => {
console.log(result)
return runAsync3()
}, (reason) => {
console.log('reason - ',reason);
return runAsync3()
}).then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
Promise 对象除了 then 方法,还有一个 catch 方法,catch(failureCallback) 是 then(null, failureCallback) 的缩略形式。它和 then 的第二个参数一样,用来指定 reject 的回调,效果和写在 then 的第二个参数一样。不过它还有另外一个作用:在执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死 js,而是会进到这个 catch 方法中。用法是这样:
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据 --')
console.log(somedata); //此处的somedata未定义
//reject(new Error('出错了'))
}, 2000)
})
return test
}
runAsync().then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
控制台打印
HomeView.vue:12 Uncaught ReferenceError: somedata is not defined
at HomeView.vue:12:19
HomeView.vue:61 -- 随便什么数据 --
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("有哪里不对了");//抛出一个失败
console.log('runAsync')
resolve('-- 随便什么数据 --')
//console.log(somedata); //此处的somedata未定义
//reject(new Error('出错了'))
}, 2000)
})
return test
}
控制台打印
Uncaught Error: 有哪里不对了
at HomeView.vue:11:13
Promise.all() 的用法
Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据 --')
//console.log(somedata); //此处的somedata未定义
//reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync2(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据222 --')
// reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync3(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('-- 随便什么数据333 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
// Promise.all([runAsync(), runAsync2(), runAsync3()]).then(results => {
// console.log(results);
// })
Promise.all([runAsync(), runAsync2(), runAsync3()]).then(([result1, result2, result3]) => {
console.log(result1, result2, result3);
})
all 接收一个数组参数,里面的值最终都返回 Promise 对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到 then 里面。all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
Promise.race() 的用法
function runAsync(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('runAsync')
resolve('-- 随便什么数据 --')
//console.log(somedata); //此处的somedata未定义
//reject(new Error('出错了'))
}, 2000)
})
return test
}
function runAsync2(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('runAsync222')
resolve('-- 随便什么数据222 --')
// reject(new Error('出错了'))
}, 1000)
})
return test
}
function runAsync3(){
const test = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('runAsync333')
resolve('-- 随便什么数据333 --')
//reject(new Error('出错了'))
}, 2000)
})
return test
}
Promise.race([runAsync(), runAsync2(), runAsync3()]).then(result => {
console.log(result)
})
就是赛跑的意思,谁跑的快,以谁为准执行回调。
在 then 里面的回调开始执行时,runAsync() 和runAsync3() 并没有停止,仍旧在执行,只是不会再进入then方法。
Promise.any()
在任意一个 Promise 被兑现时兑现;仅在所有的 Promise 都被拒绝时才会拒绝。
Promise.reject()
返回一个新的 Promise 对象,该对象以给定的原因拒绝。
Promise.resolve()
返回一个新的 Promise 对象,该对象以给定的值兑现。如果值是一个 thenable 对象(即具有 then 方法),则返回的 Promise 对象会“跟随”该 thenable 对象,采用其最终的状态;否则,返回的 Promise 对象会以该值兑现。
通常,如果你不知道一个值是否是 Promise,那么最好使用 Promise.resolve(value) 将其转换成 Promise 对象,并将返回值作为 Promise 来处理。
timeout 封装
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
wait(10000)
.then(() => alert("10 seconds"))
.catch(error => console.log(error));