同步
所谓同步代码就是当前执行代码会阻塞接下来的代码执行。
例如,执行代码A时,没有获得返回值之前,代码B无法执行。当代码A执行完成获得返回值之后,可以执行代码B。
异步
异步不阻塞代码执行。
例如,代码A执行异步过程调用发出后,不会立即获得返回结果,代码B可以继续执行。
异步编程发展
回调函数
- ajax 请求的回调
- 定时器中的回调
- 事件回调
- Nodejs 中的一些方法回调
最经典的问题:回调地狱
Promise
定义
Promise是一个对象,从中可以获取异步操作的消息
优点
解决回调地狱,避免了层层嵌套的回调函数
缺点
当链式调用过多时,可读性和维护性也很差
执行状态
- 待定(pending):初始状态,既没有被完成,也没有被拒绝
- 已完成(fulfilled):操作成功完成
- 已拒绝(rejected):操作失败
需要特别注意的是,状态改变不可逆转。
解决回调地狱的技术手段
- 回调函数延迟绑定
回调函数不是直接声明的,而是通过 then 方法传入的,即延迟传入,这就是回调函数延迟绑定 - 返回值穿透
then 中回调函数的传入值创建不同类型的 Promise,然后把返回的 Promise 穿透到外层,以供后续的调用
步骤1,2结合,产生了链式调用的效果 - 错误冒泡
前面产生的错误会一直向后传递,被最终的 catch 接收到,就不用频繁地检查错误了
静态方法
- all
Promise.all(iterable),iterable是一个可迭代对象,如Array
用于汇总多个promise结果,返回结果:
- 当所有结果成功返回时按照请求顺序返回成功
- 当其中有一个失败方法时,则进入失败方法
例如,页面需要同时发出请求进行页面渲染,就可以用 Promise.all 来实现
- allSettled
类似Promise.all
区别:执行完之后不会失败,拿到每个 Promise 的状态,而不管其是否处理成功
返回一个数组,每个Promise执行的结果,有成功fulfilled有失败rejected - any
Promise.any(iterable),iterable是一个可迭代对象,如Array
和all相反
返回结果:
- 只要参数 Promise 实例有一个变成 fulfilled 状态,最后 any 返回的实例就会变成 fulfilled 状态
- 所有参数 Promise 实例都变成 rejected 状态,包装实例就会变成 rejected 状态
- race
Promise.race(iterable),iterable是一个可迭代对象,如Array
返回结果:
- 只要参数 Promise 实例有一个变成 fulfilled 状态,最后 any 返回的实例就会变成 fulfilled 状态
- 所有参数 Promise 实例都变成 rejected 状态,包装实例就会变成 rejected 状态
- 率先改变的 Promise 实例的返回值,就传递给 race 方法的回调函数
适用于图片的超时判断
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){ resolve(img); }
img.src = 'http://www.baidu.com/img/flexible/logo/pc/result.png';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){ reject('图片请求超时'); }, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
Generator
Generator 函数是异步任务的容器,需要暂停的地方,都用 yield 语法来标注
Generator 是一个带星号的“函数”,并非真正的函数
执行流程:
-
Generator 调用,程序阻塞,不会执行任何代码
-
.next(),程序继续执行,遇到yield关键词执行暂停 -
一直执行next方法,最后返回一个对象,其存在两个属性:value 和 done两个属性
-
done 属性代表返回值以及是否完成
thunk函数
接收一定的参数,会生产出定制化的函数,最后使用定制化的函数去完成想要实现的功能
例如:
let isType = (type) => {
return (obj) => {
return Object.prototype.toString.call(obj) === `[object ${type}]`;
}
}
let isString = isType('String');
let isArray = isType('Array');
isString("123"); // true
isArray([1,2,3]); // true
这里的 isType 这样的函数就称为 thunk 函数
Generator + thunk
const fs = require('fs')
const readFileThunk = (filename) => {
return (callback) => {
fs.readFile(filename, callback);
}
}
const gen = function* () {
const data1 = yield readFileThunk('./test.txt')
console.log(111, data1.toString())
const data2 = yield readFileThunk('./test2.txt')
console.log(222, data2.toString())
}
function run(gen) {
const next = (err, data) => {
let res = gen.next(data);
if (res.done) return;
res.value(next);
}
next();
}
let g = gen();
run(g);
输出结果:
async/await
ES7推出的语法糖,号称异步的最终最优解决方案。用同步方式写异步代码。
async返回Promise对象,await控制执行顺序
上面例子,使用async / await改造
const fs = require('fs')
const readFilePromise = (filename) => {
return new Promise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
}).then(res => res);
}
const gen = async function () {
const data1 = await readFilePromise('./test.txt')
console.log(data1.toString())
const data2 = await readFilePromise('./test2.txt')
console.log(data2.toString())
}
gen()
输出结果一致