promise是ES6中引入的处理异步函数的强大特性,但是对promise的不恰当使用可能会达不到最终目的
对这个问题的探究来源于这篇文章关于promises,你理解了多少?
几个异步函数如下(resolve或reject在回调函数里被调用)
var func1 = function(last) {
return new Promise((resolve, reject)=>{
setTimeout(() => {
console.log(`last:${last}`, 1);
resolve(1);
}, 100);
});
};
var func2 = function(last) {
return new Promise((resolve, reject)=>{
setTimeout(() => {
console.log(`last:${last}`, 2);
resolve(2);
}, 200);
});
};
var func3 = function(last) {
return new Promise((resolve, reject)=>{
setTimeout(() => {
console.log(`last:${last}`, 3);
resolve(3);
}, 300);
});
};
下面几种不同写法会有什么区别?
Promise.resolve()
.then((last)=>{return func1(last);})
.then((last)=>{return func3(last);})
.then((last)=>{return func2(last);});
Promise.resolve()
.then((last)=>{func1(last);})
.then((last)=>{func3(last);})
.then((last)=>{func2(last);});
Promise.resolve()
.then(func1(last))
.then(func3(last))
.then(func2(last));
func1(last)
.then(func3(last))
.then(func2(last));
运行结果
Promise.resolve()
.then((last)=>{return func1(last);})
.then((last)=>{return func3(last);})
.then((last)=>{return func2(last);});
// last:undefined 1
// last:1 3
// last:3 2
Promise.resolve()
.then((last)=>{func1(last);})
.then((last)=>{func3(last);})
.then((last)=>{func2(last);});
// last:undefined 1
// last:undefined 2
// last:undefined 3
Promise.resolve()
.then(func1())
.then(func3())
.then(func2());
// last:undefined 1
// last:undefined 2
// last:undefined 3
func1()
.then(func3())
.then(func2());
// last:undefined 1
// last:undefined 2
// last:undefined 3
Promise.resolve()
.then(func1)
.then(func3)
.then(func2);
// last:undefined 1
// last:1 3
// last:3 2
注意到,只有第一种和最后一种写法得到我们期望的结果,因此promise的正确用法,在每一个传入then的函数的最后,都要return一个promise对象,否则会导致函数仍然按自己的节奏异步执行
注意到最后一个样例,只是把第一个样例的(last)=>{return func1(last);}变成func1,两种写法是完全等价的,而后面的显得更简洁一点
当然,promise也允许我们return一个同步的值,例如
Promise.resolve()
.then((last)=>{return func2(last);})
.then((last)=>{return 3;})
.then((last)=>{return func1(last);});
// last:undefined 2
// last:3 1
这意味着下一个then里面接收到的参数可以来自于同步,也可以来自于异步.其中执行的顺序,也不会改变
then期望得到的是一个接受若干个参数,返回一个promise或同步值的函数,如果不小心传入了一个promise,会有奇怪的结果(想法同样来自关于promises,你理解了多少?)
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then((result)=>{console.log(result);}); //输出foo
Promise.resolve('foo')
.then(Promise.resolve(1))
.then((result)=>{console.log(result);}); //输出foo
Promise.resolve('foo')
.then((result)=>{return Promise.resolve('bar');}) //输出bar
.then((result)=>{console.log(result);});
第一个结果看起来应该是bar,但却出人意料地打印出了foo。出现这个结果的原因是第一个then得到的参数并不是一个函数对象,then会把所有非函数的输入值当作then(null)
处理。因此净结果就是第一个Promise的返回值“穿透”到了最后一个then中。
因此这也是我们常说的一句话
永远都是往 then() 中传递函数!
Promise固然是ES6异步控制里最核心的组件之一,但实际应用中,我建议大家还是使用更容易阅读和使用的async/await语法糖。我们上面例子中的代码,使用async/await语法可以更清楚地写为:
f = async () => {
let last = undefined
last = await func1(last)
last = await func3(last)
last = await func2(last)
}
f()
// last:undefined 1
// last:1 3
// last:3 2
或不用async
包装
let last = undefined
last = await func1(last)
last = await func3(last)
last = await func2(last)
// last:undefined 1
// last:1 3
// last:3 2