本文主要讨论一下Promise概念以及一些使用场景和如何正确使用。本文讨论的内容大概是适合对Promise的初学者,如果你已经对Promise很熟悉了,那么没有必要看。
Promisie到底是什么玩意?
这两天参加了一个线上做题面试,有一道Promise的题目,很有意思。做题的过程中让我对Promise又有了新的理解。
Promise是什么?
Prosmise这东西本质上就是一个设计的对象,根本没有什么神秘的。Promise就像一个飞去来器,扔出去,会有两种结果:
(1)成功的飞回手上
(2)飞的时候突然爆炸了,渣都没了,只听到一声响:“reject!”
现在我们直接看一个代码:
// boomerang 是一个Promise 函数,你不用关心它如何实现的
// 你可以假设它是一个平时你能用到的HTTP call 可以返回特定的内容
// 它回成功返回数据(resolve),也可能失败(reject)
const boomerang = (s = 0, type = 'resolve', data = `飞去来器${s}型号回来了`) => {
if (type !== 'resolve') {
return new Promise((resolve, reject) =>
setTimeout(() => {
reject(new Error(`${s}毫秒后爆炸`));
}, s)
);
}
return new Promise((resolve) =>
setTimeout(() => {
resolve(data);
}, s)
);
};
const world = () => {
console.log('吃大葱');
const a = boomerang(0);
console.log(a);
const b = boomerang(1000, 'reject').catch((e) => {
return e.toString();
});
console.log(b);
const c = boomerang(2000);
console.log(c);
console.log('吃大蒜');
};
world();
那么这段代码返回什么呢?如Prosmise不是特别熟悉,你可能会想错了。你可能会误以为会返回’吃大葱‘和’ 吃大蒜‘然后后面跟着其它值是undefinied;其实不是,下面看一下返回值:
吃大葱
Promise { <pending> }
Promise { <pending> }
Promise { <pending> }
吃大蒜
Promise不是不会立即执行的吗?怎么没有返回值呢?Promise { <pending> }是什么东西?
下面我们先把Promise的基础再看一遍。Promise在JS任务队列里面是立即执行的。比如boomerang(0)这个函数,是直接排在吃大葱后面的。只是Promise的返回值是在未来返回的,如果你像这个代码一样想获得boomerang函数返回值是不行的,你只能得到return new Promise返回的结果,一个正在等待的Promise。
那么如何才能让这个代码正常工作呢?(得到a,b,c的值)
我们可以用老派的then函数一顿then()然后进行处理(不推荐,代码落后于时代而且太难看也容易出bug)。下面我们用标准的async/await重写一下:’
const world = async () => {
console.log('吃大葱');
const a = await boomerang(0);
console.log(a);
const b = await boomerang(1000, 'reject').catch((e) => {
return e.toString();
});
console.log(b);
const c = await boomerang(2000);
console.log(c);
console.log('吃大蒜');
};
world();
这样返回结果就对了。
吃大葱
飞去来器0型号回来了
Error: 1000毫秒后爆炸
飞去来器2000型号回来了
吃大蒜
这样的到了我们想要的结果,而且我们也对reject的函数正确地catch了错误。但是这个代码虽然可以正确执行,实际上还是有一些问题的。问题在于,b的值需要等1秒,c的值需要等2秒,整个world()执行用了3秒。
如何继续优化?
这里面有3个Promise,其实是可以同步执行的。下面我们就要引出Promise.all来对它们进行同步执行。改写代码如下:
const world = async () => {
console.time('test');
console.log('吃大葱');
const [a, b, c] = await Promise.all([
boomerang(0),
boomerang(1000, 'reject').catch((e) => {
return e.toString();
}),
boomerang(2000),
]);
console.log(a, b, c);
console.log('吃大蒜');
console.timeEnd('test');
};
world();
看一下执行结果:
吃大葱
飞去来器0型号回来了 Error: 1000毫秒后爆炸 飞去来器2000型号回来了
吃大蒜
test: 2005.940ms
所以在实际工作的代码中,如果有多个Promise不是互相依赖的,要善用Promise.all来进行优化。你可能对于代码中没有catchPromise.all进行catch的错误抱有疑虑。其实在这里如果我们对a,b,c的函数调用都进行了catch,就不必对Promise.all进行catch。这里需要加深理解。
暂时先写到这里。可能还需要写一下race什么的。