一 回调函数
异步操作的最基本方法是回调函数
ajax(urlString, callback){
//回调函数执行前操作
callback()
}
callback(){
//处理逻辑
}
但是容易造成回调地狱
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
并且回调函数高度耦合,程序结构混乱,不能使用 try catch 捕捉错误,不能直接return
二 Promise
Promise状态
promise对象有三种状态
- pending——Promise对象实例创建时候的初始状态
- Fulfiied—— Promise对象成功执行后的状态
- Rejected——Promise对象失败后的状态
promise 构造器函数
promise的构造器函数接受一个函数做为参数,该函数的两个参数为resolve和reject,异步操作成功后,调用 resolve 函数,将promise对象的状态从 pending 转化为 Fulfied ,失败则调用 reject 函数 将状态 pending 转化为 rejected
const promise = new Promise(function(resolve, reject) {
// code
if (// 异步操作成功 //){
resolve(value);
} else {
reject(error);
}
});
promise的链式调用
Promise.prototype.then()
Promise中then方法是定义再原型对象Promise.prototye上的,then() 方法有两个参数,一个是resolved状态的回调函数,一个是rejected状态的回调函数
promise.then(function(value){
console.log(value);
},function(err){
console.log(err);
})
Promise.prototype.catch()
异步操作抛出错误时,状态变为rejected,调用catch()指定的回调函数,也就是被catch() 方法捕获
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
Promise.prototype.finally()
与try catch中finally类似,不管Promise对象状态如何,都会进入finally中
Promise.all()
该方法可以将多个Promise实例转化为一个新的Promise实例
const p = Promise.all([p1, p2, p3]);
此时p的状态由 p1 p2 p3 共同决定
- p1 p2 p3 状态全为fulfilled, p的状态才会变成fulfilled,此时p的状态才会为 fulfilled,p1 p2 p3的返回值会组成一个数组传递给p的回调函数
- p1 p2 p3 中只要一个状态为rejected,p的状态会变为 rejected。其中第一个为rejected的实例的返回值传递给p的回调函数
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json");
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
手写简易promise.all()
function all(arr){
return new Promise(res,rej){
const result = [];
const resNum = 0;
const length = arr.erngth
if(length==0) return new Promise(arr);
arr.forEach(item=>{
item.then(resolve=>{
result.push(resolve);
count++;
if(count == length){ //全为fullfied 返回结果
res(result);
}
}).catch(err=>{
rej(err);
})
})
}
}
promise.race()
p1、p2、p3之中第一个改变状态的,p的状态就跟着其改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
const p = Promise.race([p1, p2, p3]);
Generators/yield
Generators可以控制函数的执行,是一种状态机,封装了多个内部状态
- Generators还是一个遍历器对象生成函数
- yield可暂停函数,next可启动函数,每次返回的时yield后的表达式结果
- yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
function* funG() {
yield console.log(1)
yield console.log(2)
yield console.log(3)
}
function run() {
console.log(4)
}
const iter = funG()
iter.next()
run()
iter.next()
iter.next()
iter.next()
// 结果:
1
4
2
3
{value: undefined, done:true}
异步解决
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw('出错了');
async/await
async 为Generator函数的语法糖
经典例子:
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async函数是Generator函数 * 号变成 async,将yield换成await,另外也有许多特点
- async自带执行器,直接与普通函数一样执行即可,Generator函数必须依靠执行器
- async 返回Promise对象 ,再使用await可以返回该Promise对象结果而Generator函数返回Iterator对象
原理
async 函数就是将Generator 函数和自动执行器,包装在一个函数里
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
spawn函数是自动执行器的翻版
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}