Promise
产生:改善 Callback Hell 问题
什么样的方法可以用 Promise 重写
例 1:
mysql.query('SELECT 1 + 1 AS solution', function (err, rows, fields) {
if (err) {
throw err;
}
// 对查询结果进行操作
console.log('The solution is: ', rows[0].solution);
});
例 2:
fs.readFile('myfile.txt', function (err, file) {
if (err) {
throw err;
}
// 对file进行操作
});
如何将一个回调方法改为 Promise
Promisify
一般情况下,该方式首选。
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
stat('.')
.then((stats) => {
// Do something with `stats`
})
.catch((error) => {
// Handle the error.
});
new Promise()
虽然所有的最终实现原理应该都是这样,但这种写法上的话,又再次出现了地狱回调的问题,可读性非常差。
const fs = require('fs');
const stat = (...args) =>
new Promise((resolve, reject) => {
fs.stat(...args, (err, stats) => {
if (err) {
reject(err);
} else {
resolve(stats);
}
});
});
stat('.')
.then((stats) => {
// Do something with `stats`
})
.catch((error) => {
// Handle the error.
});
Defer
兼顾了可读性的一种写法。
const fs = require('fs');
const getDefer = () => {
const deferred = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
};
const stat = (...args) => {
const deferred = getDefer();
fs.stat(...args, (err, stats) => {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(stats);
}
});
return deferred.promise;
};
stat('.')
.then((stats) => {
// Do something with `stats`
})
.catch((error) => {
// Handle the error.
});
如何调用 Promise 方法
直接执行
promiseFn()
.then()
.catch()
.then() // 此处的 .then 未经过异常捕获
.finally(); // 不管成功失败,最终都将执行
批量执行
Promise.all([promiseFn1(), promiseFn2()]).then();
Promise.allSettled(); // 推荐
Promise.race();
Promise.any();
async / await
自 es7 时代新增的语法糖。其主要应用场景是对于 Promise 方法调用的再次封装
。
什么样的方法应该用 async / await 包裹
示例:
// 原始代码
function query() {
return mysql.query('SELECT 1 + 1 AS solution').then((rows) => {
return rows[0].solution;
});
}
可使用该语法糖减少嵌套:
async function query() {
const [{ solution } = {}] = await mysql.query('SELECT 1 + 1 AS solution');
return solution;
}
什么样的方法不需要 async / await 包裹
示例:
async function query() {
// 一些同步操作
const result = await mysql.query('SELECT 1 + 1 AS solution');
return result;
// 或者直接:
// return await mysql.query('SELECT 1 + 1 AS solution');
}
其效果等同于:
function query() {
// 一些同步操作
return mysql.query('SELECT 1 + 1 AS solution');
}
并且,加上 async / await 包裹之后性能更低,因为多了一层无意义的语法糖嵌套。
并发执行
多个await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
const foo = await getFoo();
const bar = await getBar();
上面代码中,getFoo
和getBar
是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo
完成以后,才会执行getBar
,完全可以让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
上面两种写法,getFoo
和getBar
都是同时触发,这样就会缩短程序的执行时间。
p.s. 如果有多个 Promise 任务需要并发执行,建议不要使用或者慎重使用 Promise.all()
方法。