Promise简单介绍
Promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件——更合理和更强大,Promise 是一个对象,从它可以获取异步操作的消息,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve 和reject, 基本样例:
-
const promise = new Promise((resolve, reject)=>{
-
ses.sendEmail(params, function(err, data) {
-
if (err) {
-
reject (err);
-
} else {
-
resolve(data);
-
}
-
});
-
});
Promise实例生成以后,可以用then 方法分别指定resolved 状态和rejected 状态的回调函数。
-
promise.then(function(value) {
-
resolved(value);
-
}, function(error) {
-
rejected(error);
-
});
但是一般不要在then方法里面定义reject状态的回调函数,强烈建议使用catch的方式进行处理,上面的写法可以改写为下面这种更加合理:
-
promise.then(function(data) {
-
resolved(value);
-
}).catch(function(err) {
-
console.log(error)
-
});
因为改写后的方式,既可以捕获前面then 方法执行中的错误,也更接近同步的写法(try/catch )
async/await
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
async有很多种使用形式:
-
// 函数声明
-
async function foo() {}
-
-
// 函数表达式
-
const foo = async function () {};
-
-
// 对象的方法
-
let obj = { async foo() {} };
-
obj.foo().then(...)
-
-
// Class 的方法
-
class Storage {
-
constructor() {
-
this.cachePromise = caches.open('avatars');
-
}
-
-
async getAvatar(name) {
-
const cache = await this.cachePromise;
-
return cache.match(`/avatars/${name}.jpg`);
-
}
-
}
-
-
const storage = new Storage();
-
storage.getAvatar('jake').then(…);
-
-
// 箭头函数
-
const foo = async () => {};
我们可以测试一下async的返回值,写一个test.js
-
async function testAsync() {
-
return "hello async";
-
}
-
-
const result = testAsync();
-
console.log(result);
MacdeMac-mini:work mac$ node test.js
Promise { 'hello async' }
运行之后发现,输出的是一个Promise对象,由此可知async 函数返回的是一个 Promise 对象,实际上async/await就是为了解决Promise写异步时then调用链的问题,如果我们在一个普通的函数里面没法使用await(await必须要在async函数里面才可以使用)来获取async函数的返回值的话,我们就可以用then的方式进行处理
-
testAsync().then(v => {
-
console.log(v); // 输出 hello async
-
});
我们可以认为await实际上是在等待async函数执行完成,因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,它等的实际是一个返回值。
-
async function testAsync() {
-
return "hello async";
-
}
-
-
async function test() {
-
const value = await testAsync();
-
console.log(value);
-
}
-
test();
当函数执行的时候,一旦遇到await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
一个函数要调用一个异步方法,同时该函数需要有返回值,我们可以这么写
-
exports.handler = async (event) => {
-
var params = ""
-
const result = await test_async(params);
-
return result;
-
};
-
-
function test_async(params){
-
const promise = new Promise((resolve, reject)=>{
-
ses.sendEmail(params, function(err, data) {
-
if (err) {
-
reject (err);
-
} else {
-
resolve(data);
-
}
-
});
-
});
-
return promise;
-
-
}
上面的test_async这个方法并没有写async,但是实际上由于test_async这个方法本身返回的就是一个Promise对象,我们知道async所起的作用就是返回一个Promise的(上面提到过),所以这个地方可以不用写async效果一样的。然后我们就可以使用await来获取test_async的返回值,并且作为函数的return返回
Promise和Async/Await两种方式比较
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于之前每个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:
-
function takeLongTime(n) {
-
return new Promise(resolve => {
-
setTimeout(() => resolve(n + 200), n);
-
});
-
}
-
function step1(n) {
-
console.log(`step1 with ${n}`);
-
return takeLongTime(n);
-
}
-
-
function step2(m, n) {
-
console.log(`step2 with ${m} and ${n}`);
-
return takeLongTime(m + n);
-
}
-
-
function step3(k, m, n) {
-
console.log(`step3 with ${k}, ${m} and ${n}`);
-
return takeLongTime(k + m + n);
-
}
把这个需求分别用Promise和async/await进行改造
Promise使用Then来改造:
-
function doIt() {
-
console.time("doIt");
-
const time1 = 300;
-
step1(time1)
-
.then(time2 => {
-
return step2(time1, time2)
-
.then(time3 => [time1, time2, time3]);
-
})
-
.then(times => {
-
const [time1, time2, time3] = times;
-
return step3(time1, time2, time3);
-
})
-
.then(result => {
-
console.log(`result is ${result}`);
-
console.timeEnd("doIt");
-
});
-
}
-
-
doIt();
使用async/await进行改造:
-
async function doIt() {
-
console.time("doIt");
-
const time1 = 300;
-
const time2 = await step1(time1);
-
const time3 = await step2(time1, time2);
-
const result = await step3(time1, time2, time3);
-
console.log(`result is ${result}`);
-
console.timeEnd("doIt");
-
}
-
-
doIt();
可以看出来async更加简单
async注意事项
1:如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。所以最好把await命令放到try catch中进行处理
-
async function myFunction() {
-
try {
-
await test_async();
-
} catch (err) {
-
console.log(err);
-
}
-
}
当然也可以这么写:
-
async function myFunction() {
-
await test_async()
-
.catch(function (err) {
-
console.log(err);
-
});
-
}
但是建议还是使用第一种方式比较好,更加符合同步的处理方式。
如果有多个await,可以统一的放到try catch处理。
2:多个await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
-
let foo = await getFoo();
-
let bar = await getBar();
上面代码中,getFoo 和getBar 是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo 完成以后,才会执行getBar ,完全可以让它们同时触发。
-
// 写法一
-
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
-
3:await 命令只能用在async 函数之中,如果用在普通函数,就会报错
4:如果在调用async函数的时候没有写await,那么他会被立刻执行,因为async返回的是Promise对象,而Promise的特点是无等待,那么也就是说在没有await的情况下执行async函数,该函数会立马执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
-
router.get('/', function(req, res) {
-
const html = test_async();
-
console.log(html);
-
res.sendWithLog(html);
-
});
-
-
async function test_async() {
-
var value = 'hello async';
-
return value;
-
}
输入http://127.0.0.1:8080/test,前端得不到我们要的值,控制台打印的是一个Promise对象,因为调用test_async的这个方式是一个非async的方法,所以不能用await,前面提到过如果没法写await,这个时候可以使用then来获取返回值。
我们可以改造一下,在调动test_async的时候加上await再看下效果:
-
router.get('/test', function(req, res) {
-
get_v(res);
-
});
-
async function get_v(res) {
-
const html = await test_async();
-
res.sendWithLog(html);
-
}
-
-
async function test_async() {
-
var value = 'hello async';
-
return value;
-
}
此时输入http://127.0.0.1:8080/test,前端得到的是hello async,得到了我们想要的值 --------------------- 本文来自 总有刁明想害朕 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/Crystalqy/article/details/80586494?utm_source=copy |