Promise的注意点, 以及相关题目

本文深入探讨Promise的工作原理,包括状态的不可逆性、resolve/reject的执行机制、then/catch的链式调用、错误处理策略及Promise.all的用法。通过实例解析,帮助读者掌握Promise的高级应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

昨天面试去xiaoman, 提到了promise;
面对不断深入发问, 我脑子一片空白, 然后就想到了白云山;
我想怎么思考其他问题的时候没有这么思维活跃;

Promise 状态的不可逆性

Promise状态的一旦变成resolved或rejected时,Promise的状态和值就固定下来了,不论你后续再怎么调用resolve或reject方法,都不能改变它的状态和值。

var p1 = new Promise(function(resolve, reject){
  resolve("success1");
  resolve("success2");
});

var p2 = new Promise(function(resolve, reject){
  resolve("success");
  reject("reject");
});

p1.then(function(value){
  console.log(value);
});

p2.then(function(value){
  console.log(value);
});
// "success1"
// "success"
resolve或reject后面的代码会不会执行

当然会

then的第二个参数和catch

catch相当于then(null,function(err){ /*处理错误*/})的写法

// 写法一
new Promise((resolve)=>{
	foo.bar()
}).then(res=>{
	console.log(res)
},err=>{
	console.log(err)
})
// ReferenceError: foo is not defined
// 写法二
new Promise((resolve)=>{
	foo.bar()
}).then(res=>{
	console.log(res)
	kk()
}).catch(err=>{
	console.log(err)
})
// ReferenceError: foo is not defined

上面代码中,catch要好于then(null,err=>{})写法, catch可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。如下:

new Promise((resolve)=>{
	resolve(1)
}).then(res=>{
	console.log(res)
	kk()
}).catch(err=>{
	console.log(err)
})
// ReferenceError: kk is not defined
then/catch的链式调用

首先, 在new Promise中需要执行resolve或者reject改变状态,否则不会执行then方法, 像下面then就不会执行:

var p = new Promise(function(resolve, reject){
  return 1
});
p.then(function(value){              
  console.log(value);
  return value*2;
}).then(function(value){             
  console.log(value);
})
// Promise {<pending>}

在状态改变后, then方法返回一个新的Promise对象,因此可以通过链式调用then方法.
catch也可以链式调用, 因为catch相当于then(null,err=>{})

函数的返回值将被用作创建then返回的Promise对象, 有如下情况:

  • return 一个同步的值(当没有返回时,默认返回undefined),then方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。
  • return 一个 Promisethen方法将根据这个Promise的状态和值创建一个新的Promise对象返回。
  • throw 一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。
var p = new Promise(function(resolve, reject){
  resolve(1);
});
p.then(function(value){               //第一个then
  console.log(value);
  return value*2;
}).then(function(value){              //第二个then
  console.log(value);
}).then(function(value){              //第三个then
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){              //第四个then
  console.log(value);
  return Promise.reject('reject');
}).then(function(value){              //第五个then
  console.log('resolve: '+ value);
}, function(err){
  console.log('reject: ' + err);
})
1
2
undefined
"resolve"
"reject: reject"

再写一个return返回promise的例子:

var a = new Promise(resolve=>{
	setTimeout(()=>{
		resolve('5s later')
	},5000)
})
var p = new Promise(function(resolve, reject){
  resolve(1);
});
p.then(function(value){               //第一个then
	console.log(1)
  	return a
}).then(function(value){              //第二个then
  console.log(value);
})


// 1
// Promise {<pending>}
// 5s later
Promise.resolve()

Promise.resolve()(注意,是Promise.resolve(),而不是new Promise中的resolve)可以接收一个值或者是一个Promise对象作为参数。
当参数是普通值时,它返回一个resolved状态的Promise对象;
当参数是Promise对象时,对象的值就是这个参数;
下面的例子可以看到p1,p2是相等的

let p1 = Promise.resolve(1)
let p2 = Promise.resolve(p1)
p1 === p2
// true
then拿到数据发现不合适, 怎么触发catch

throw new Error()抛出错误. 或者 Promise.reject()

resolve和reject

resolve的参数是一个Promise对象时,resolve会"拆箱"获取这个Promise对象的状态和值,但这个过程是异步的。
即最后的状态是根据传入的promise对象的状态确定的;
如下: 执行的是resolve(a), 但是a5秒后返回的状态是rejected,所以会执行rejected回调

var a = new Promise((resolve,reject)=>{
	setTimeout(()=>{
		reject('error')
	},5000)
})
var p = new Promise(function(resolve, reject){
  	var b = resolve(a)
	console.log(a === b)
});
p.then(function(value){              
	console.log('success')
},function(err){              
	console.log(err)
	return err
})
// false
// Promise {<pending>}
// error

但是, reject方法不同, reject方法不存在拆箱等待, 而是直接将传入的promise对象传入下个阶段, 如下

var a = new Promise((resolve,reject)=>{
	setTimeout(() => {
		reject('error after 5s')
	},5000)
})

var p = new Promise(function(resolve, reject){
  	reject(a)
})

p.then(function(value){              
	console.log('success')
}).catch(err1=>{                  // 第一个 catch 
	console.log('fail: ' + err1)
	return err1
}).catch(err2=>{                  // 第二个 catch 
	console.log(err2)
	return 1
}).then(res=>{
	console.log(res)
})

// fail: [object Promise]
// Promise {<pending>}
// error after 5s
//  1

可以看到, prejecta, 直接把a传到了第一个 catch, 第一个catch中, 打印出来参数, 就是promise对象a .
在 第一个catchreturn err1, 等待5秒后a变成rejected状态, 触发第二个 catch , 捕获到结果error after 5s,
最后catch 可以继续链式调用, 返回1, 相当于resolve(1), 最后then打印出1.

promise中出现异常, promise外的后面的代码会执行吗

会, Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。

new Promise(function(resolve, reject){
  resolve(x+1);
});
console.log(333)
// 333
// Uncaught (in promise) ReferenceError: x is not defined
promise.all
let a = new Promise(resolve=>{setTimeout(()=>{resolve(111)},3000)})
let b = new Promise(resolve=>{setTimeout(()=>{resolve(222)},1000)})
let c = new Promise(resolve=>{setTimeout(()=>{resolve(333)},100)})

Promise.all([b,a,c]).then(res=>{
	console.log(res)

})
// 结果数组也是顺序的
// [222, 111, 333]
let a = new Promise(resolve=>{setTimeout(()=>{console.log('haha');resolve(111);},3000)})
let b = new Promise((resolve,reject)=>{setTimeout(()=>{reject(222)},1000)})
let c = new Promise(resolve=>{setTimeout(()=>{resolve(333)},100)})

Promise.all([b,a,c]).then(res=>{
	console.log(res)
}).catch(err=>{
	console.log(err)
})
只要有一个错误,就会catch该错误
// 222
// haha
Promise如何按顺序执行

有一个数组,数组元素是返回Promise对象的函数,怎样顺序执行数组中的函数,即在前一个数组中的Promise resolve之后才执行下一个函数?

  • 先生成数据, 下面代码执行完,生成10个函数组成的数组, 均返回promise对象
function createPromises(id) {
    return function () {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(id)
                resolve(id)
            }, 1000)
        })
    }
}
 
let list = []

for(let i = 0; i < 10; i++) {
    list.push(createPromises(i))
}
// (10) [ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]
  • 递归法
// 递归调用
function promise_queue(list, i) {
    if (i >= 0 && i < list.length) {
        list[i]().then(() => {
            promise_queue(list, i + 1)
        })
    }
}
promise_queue(list, 0)
  • await
async function promise_queue(list) {
    let index = 0
    while (index >= 0 && index < list.length) {
        await list[index]()
        index++
    }
}
promise_queue(list)
  • Promise.resolve()
    初始状态是Promise.resolve(), then执行第一个, 并把下一个作为thensuccess函数(因为then返回一个promise的话, promise状态改变后, 才会执行下一个then)
function promise_queue(list) {
  var sequence = Promise.resolve()
  list.forEach((item) => {
    sequence = sequence.then(item)
  })
  return sequence
}

  • reduce
	list.reduce((pre,next)=>pre.then(next),Promise.resolve()); 
promise实现每隔一段时间执行一次函数, 执行n次 , 不用await

每隔interval毫秒, 执行一次fn函数, 执行time次:

function interval(fn, interval, time){
	 let f = () => new Promise(resolve=>{
		setTimeout(()=>{
			fn()
			resolve()
		},interval)
	})
	let a = Promise.resolve()
	for(let i = 0; i< time; i++){
		a = a.then(f)
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值