文章目录
什么是Promise
Promise对象是一个构造函数
Promise是异步编程的一种解决方案
promise有几个状态
promise状态有pending、resolve、reject(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变
- 1.初始化状态:pending
- 2.成功状态,调用resolve
- 3.失败状态,调用reject
promise的优缺点
优点:
1.Promise 分离了 异步数据获取 和 业务逻辑,有利于代码复用。
2.可以采用链式写法,避免回调地狱。
缺点:
1.代码逻辑顺序与执行顺序不一致,不利于阅读与维护
。
2. 一旦新建它就会立即执行,无法中途取消
。
3. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
。
4. 当处于padding状态时,无法获取到底是处于哪个阶段,是刚开始还是已经结束。
promise是异步不是同步
promise本身是同步的
Promise作用:解决异步回调的问题
promise简介
- promise简单例子
function test(resolve, reject) {
var timeOut = Math.random() * 2;
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}
var p1 = new Promise(test);
var p2 = p1.then(function (result) {
console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
console.log('失败:' + reason);
});
变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功/失败时,我们告诉Promise对象
上面代码可简化为
function test(resolve, reject) {
var timeOut = Math.random() * 2;
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}
new Promise(test).then(function (result) {
console.log('成功:' + result);
}).catch(function (reason) {
console.log('失败:' + reason);
});
可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了
resolve方法是把状态设置为成功,执行then方法
reject方法是把状态设置为失败,执行catch方法
- Promise还可以做多层嵌套,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务(任务1或任务2)失败则不再继续,并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:
//异步方法一
function getone(resolve,reject){
setTimeout(function(){
resolve("gettwo");
},3000)
}
//异步方法二
function gettwo(resolve,reject){
setTimeout(function(){
resolve("getthree");
},3000)
}
//异步方法三
function getthree(resolve,reject){
var ran = 0;
setTimeout(function(){
let ranStr = Math.random().toFixed(2);
ran = Number(ranStr);
console.log('---------------');
console.log(ran)
if(ran>0.5) reject("getthree");
else resolve('getthree');
},3000)
}
var result = new Promise(getone)
.then(function(resulttwo){
console.log('----------two------------');
console.log(resulttwo);
return new Promise(getthree);
})
.then(function(resultthree){
console.log('-----------three---------');
console.log(resultthree);
})
.catch(function(err){
console.log('-----------error---------');
console.log(err);
})
- Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:(同时满足多个条件后才执行结果函数)
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
Promise的六大方法
-
Promise.resolve
静态方法Promise.resolve(value)可以认为是new Promise()方法的快捷方式,比如Promise.resolve(42)可以认为是以下代码的语法糖:new Promise(function(resolve){ resolve(42) })
这个静态方法会让Promise对象立即进入resolved 状态,并将参数传递给后面then方法。
简单总结一下Promise.resolve方法的话,它的作用就是将传递给它的参数填充到promise对象后并返回这个promise对象。
-
Promise.reject
Promise.reject(error)是和Promise.resolve(value)类似的静态方法,是new Promise()方法的快捷方式。比如Promise.reject(new Error(“出错了”))就是下面代码的语法糖形式:new Promise(function(resolve,reject){ reject(new Error("出错了")); });
简单总结一下Promise.reject方法的话:它的功能是调用该promise对象通过then指定的onRejected函数,并将错误(Error)对象传递给这个onRejected函数
-
Promise.then
promise.then(onFulfilled, onRejected)① 回调函数异步执行
var promise = new Promise(function (resolve){ console.log("inner promise"); // 1 resolve(42); }); promise.then(function(value){ console.log(value); // 3 }); console.log("outer promise"); // 2
② 返回值 ----- 新创建的Promise对象
不管你在回调函数onFulfilled中会返回一个什么样的值,或者不返回值,该返回值都会由Promise.resolve(return的返回值)进行相应的包装处理,因此,最终then的结果都是返回一个新创建的promise对象。正是then函数中有了这样返回值的机制,才能使得在整个Promise链式结构当中,每个then方法都能给下一个then方法传递参数
var aPromise = new Promise(function (resolve) { resolve(100); }); var thenPromise = aPromise.then(function (value) { console.log(value); }); var catchPromise = thenPromise.catch(function (error) { console.error(error); //不执行 });
③ promise穿透
我们先来举个例子:Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) { console.log(result); });
如果你认为输出的是 bar,那么你就错了。实际上它输出的是 foo!
产生这样的输出是因为你给then方法传递了一个非函数(比如promise对象)的值,代码会这样理解:then(null),因此导致前一个promise的结果产生了坠落的效果,也就是和下面的代码是一样的,代码直接穿透了then(null)进入了下一层链:
Promise.resolve('foo').then(null).then(function (result) { console.log(result); });
-
Promise.catch
相当于promise.then(onFulfilled, onRejected)中的onRejected函数
-
Promise.race()
实现:「谁跑的快,以谁为准执行回调」
使用案例:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作//请求某个图片资源 function requestImg(){ var p = new Promise(function(resolve, reject){ var img = new Image(); img.onload = function(){ resolve(img); } img.src = 'xxxxxx'; }); return p; } //延时函数,用于给请求计时 function timeout(){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ reject('图片请求超时'); }, 5000); }); return p; } Promise .race([requestImg(), timeout()]) .then(function(results){ console.log(results); }) .catch(function(reason){ console.log(reason); });
race方法,执行较快的那个,执行then()。执行较慢的那个,执行结果将被丢弃。
-
Promise.all
Promise.all接收一个promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用.then方法。
传递给Promise.all的promise并不是一个个的顺序执行的,而是同时开始、并行执行的,我们可以举个例子:
let arr = [1000, 3000, 5000, 7000] let promiseArr = [] for(let i = 0; i < arr.length; i++ ) { let newPromise = new Promise((resolve, reject) => { setTimeout(()=> { console.log(arr[i]) resolve(arr[i]) }, arr[i]) }) promiseArr.push(newPromise) } Promise.all(promiseArr).then(res => { console.log(res) }).catch(err =>{ console.log(err) })
如果所有的Promise中只有一个执行错误,那么整个Promise.all不会走Promise.all().then()这个流程了,而是走Promise.all().catch()这个流程
异常捕获的三种方法
- then函数中传递第二个函数作为第二个参数,用来执行reject函数
- catch函数
- finally ,不论成败,resolve/reject,最后都会执行
//捕获异常一,then中传递第二个函数,用来执行reject函数
new Promise((resolve,reject)=>{
let random = parseInt(Math.random()*10);
setTimeout(()=>{
if(random>=9) resolve(random);
else reject(random)
})
}).then((val)=>{
let pow2 = Math.pow(val,2);
console.log('success',pow2)
},(err)=>{
console.log('error',err);
})
//捕获异常二,catch函数
new Promise((resolve,reject)=>{
let random = parseInt(Math.random()*10);
setTimeout(()=>{
if(random>=9) resolve(random);
else reject(random)
})
}).then((val)=>{
let pow2 = Math.pow(val,2);
console.log('success2',pow2)
}).catch((err)=>{
console.log('error2',err);
})
// finally 最后执行
new Promise((resolve,reject)=>{
let random = parseInt(Math.random()*10);
setTimeout(()=>{
if(random>=6) resolve(random);
else reject(random)
})
}).then((val)=>{
let pow2 = Math.pow(val,2);
console.log('success3',pow2)
}).catch((err)=>{
console.log('error3',err);
}).finally(()=>{
console.log('finally,不论成败,都会执行!')
})
Promise链式调用中then/catch方法的返回值
1、概述
then() 方法返回一个 Promise对象
。
它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
const promise1 = new Promise((resolve, reject) => {
resolve('Success!');
//reject('fail');
});
promise1.then((value) => { // 成功回调
console.log(value);
},(err)=>{ //失败回调
console.log(err);
});
2、then() 方法的返回值
如果 then 中的回调函数:
-
返回了一个值,或没有返回任何值(undefined),那么
then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值
。即相当于返回Promise.resolve(返回的值/undefined)
。Promise.resolve(33).then((data)=>{ console.log(data) // 33 return 100; }).then((val)=>{ console.log(val) ; // 100 }).then((res)=>{ console.log(res); // undefined })
最终整体返回一个Promise对象
-
抛出一个错误,那么
then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值
。Promise.resolve(33).then((data)=>{ throw "error"; // 抛出错误,执行reject }).then((val)=>{ console.log(val) ; //不执行 }).then((res)=>{ console.log(res); //不执行 }).catch(err=>{ console.log(err) ; // error 未捕获到错误之前的不执行,捕获到错误,然后继续向下执行 return "success"; }).then(res=>{ console.log(res); // success })
扔出错误(throw ''
),则在错误被捕获之前的代码将不执行,一旦错误被捕获,则继续向下执行。
- 返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('timeout') },5000) }) Promise.resolve(33).then((data)=>{ return p1; }).then(res=>{ console.log(res); // 打印 timeout })
- 返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('error') },5000) }) Promise.resolve(33).then((data)=>{ return p1; }).then(res=>{ console.log(res); // 不执行 }).catch((err)=>{ console.log(err); // 打印 error })
- 返回一个未定状态(pending)的 Promise,那么
then 返回 Promise 的状态也是未定的
,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
返回的Promise也是pending状态let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('5000s'); },5000) }) Promise.resolve(33).then((data)=>{ return p1; }).then(res=>{ console.log(res); // 不执行 }).catch((err)=>{ console.log(err); // 不执行 }).finally(()=>{ console.log(123) // 不执行 })
参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
如何生成一个Promise对象
// 方法一,new Promise(异步函数)
let p1 = new Promise(resolve => {
resolve('成功1!');
});
// 方法二,Promise.resolve(值) ,返回一个promise对象
let p2 = Promise.resolve('成功2!');
function printContent(params) {
console.log(params);
}
p1.then(printContent);
p2.then(printContent);
如何中断Promise的链式调用
-
方法一、通过抛出一个异常来终止,即
throw 一个异常
当Promise链中抛出一个异常时,错误信息沿着链路向后传递,直至被捕获,错误捕获前的函数不会被调用。
但是如果错误被捕获后,其后面还有链式调用时,仍然会被调用链式调用的最后捕获错误,用来终止调用
let needBreak = true; let p = new Promise((resolve, reject) => { resolve('step1'); }); p.then(data => { console.log(data); return 'step2'; }).then(data => { console.log(data); if (needBreak) { throw "we need break"; // 抛出异常 } return 'step3'; }).then(data => { console.log(data); return 'step4'; }).catch(reason => { // 接收异常消息 console.log('got error:', reason); }).finally(() => { // 不论成败,才会执行 console.log('finished.'); });
这时候的输出就成了这样,没有输出step3
链式调用的中间和最后都去捕获错误
(抛出的错误,在被捕获之前,所有的链式调用都不会被调用,但一旦被捕获,之后的链式调用则仍然可以调用),案例如下:
Promise.resolve().then(() => {
console.log('ok1')
throw 'throw error1' // 抛出异常1
}).then(() => {
console.log('ok2')
}, err => {
console.log('err->', err) // 捕获错误1,继续向下执行
}).then(() => {
// 该函数将被调用
console.log('ok3')
throw 'throw error3' // 再次抛出异常2
}).then(() => {
console.log('ok4') // 错误捕获前的函数不会被调用
}).catch(err => {
console.log('err->', err) //捕获错误2
})
输出如下
-
方法二、通过返回一个
Promise.reject() 来中断
let needBreak = true; let p = new Promise((resolve, reject) => { resolve('step1'); }); p.then(data => { console.log(data); return 'step2'; }).then(data => { console.log(data); if (needBreak) { return Promise.reject('break without exception.'); // 返回一个 Promise.reject() 异常 } return 'step3'; }).then(data => { console.log(data); return 'step4'; }).catch(reason => { console.log(reason); }).finally(() => { console.log('finished.'); });
这时候的输出就成了这样,没有输出step3
-
方法三、利用,当新对象保持“pending”状态时,原Promise链将会中止执行
Promise.resolve().then(() => { console.log('ok1') return new Promise(()=>{}) // 返回“pending”状态的Promise对象 }).then(() => { // 后续的函数不会被调用 console.log('ok2') }).catch(err => { console.log('err->', err) })
结果如下
-
方法四、Promise.race竞速方法
let p1 = new Promise((resolve, reject) => { resolve('ok1') }) let p2 = new Promise((resolve, reject) => { setTimeout(() => {resolve('ok2')}, 10) }) Promise.race([p2, p1]).then((result) => { console.log(result) //ok1 }).catch((error) => { console.log(error) })
结果如下:
旧浏览器没有 Promise 怎么办
可以使用es6-promise-polyfill
来作兼容。
es6-promise-polyfill
可以使用页面标签直接引入,当然也可以通过es6的 import方法引入。
引入这个 es6-promise-polyfill
之后,它会在 window对象中加入 Promise对象。这样我们就可以全局使用Promise了
作者:小马_vh
链接:https://juejin.im/post/6844903830119792647
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
参考链接:https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000#0