# Async/Await如何同步方式实现异步
异步:js是单线程的,执行代码是一行一行的往下走(同步),但在实际应用中,会有大量的网络请求,它的响应时间是不确定的,这种情况下一直等待显然是不行的,因而 js 设计了异步,即 发起网络请求(诸如 IO 操作,定时器),由于需要等服务器响应,就先不理会,而是去做其他的事儿,等请求返回了结果的时候再说(即异步)。
Async/Await是实现异步非常优雅的方式,不会像callback那样产生回调地狱,不会像promise.then那样一直写.then……而是采用了同步代码的书写方式,完成了异步操作。例如我们在写await A()
时,代码还会继续往下执行,当A()结果返回时,会拿到对应结果执行对应的操作。
async/await 是参照 Generator 封装的一套异步处理方案,可以理解为 Generator 的语法糖,而 Generator 又依赖于迭代器Iterator:
(1)下面看看迭代器Iterator的原理和实现:
当一个对象只有满足下述条件才会被认为是一个迭代器,它实现了一个 next() 的方法,该方法必须返回一个对象,对象有两个必要的属性:
- done(bool)
- true:迭代器已经超过了可迭代次数。这种情况下,value 的值可以被省略
- false:如果迭代器可以产生序列中的下一个值为false,等效于没有指定 done 这个属性
- value 迭代器返回的任何 JavaScript 值。done 为 true 时可省略
const makeIterator = arr => {
let nextIndex = 0;
return {
next: () =>
nextIndex < arr.length
? { value: arr[nextIndex++], done: false }
: { value: undefined, done: true },
};
};
const it = makeIterator(['人月', '神话']);
console.log(it.next()); // { value: "人月", done: false }
console.log(it.next()); // { value: "神话", done: false }
console.log(it.next()); // {value: undefined, done: true }
(2)下面看看生成器Generator的原理和实现:
Generator:生成器对象是生成器函数(GeneratorFunction)返回的,它符合可迭代协议和迭代器协议,既是迭代器也是可迭代对象,可以调用 next 方法,但它不是函数,更不是构造函数。
一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的迭代器对象,当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现 yield 的位置为止(让执行处于暂停状),yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行),调用 next() (再启动)方法时,如果传入了参数,那么这个参数会作为上一条执行的 yield 语句的返回值。
用 Generator 实现异步操作:
// 生成器函数
const gen = function* () {
const res1 = yield new Promise((resolve)=>resolve(111))
console.log(res1);
const res2 = yield new Promise((resolve)=>resolve(222))
console.log(res2);
};
const g = gen();
// 第1个yield暂停
const g1 = g.next();
console.log('g1:', g1); // g1: { value: Promise { 111 }, done: false }
g1.value
.then(res1 => {
console.log('res1:', res1); // res1: 111
// 第2个yield暂停
const g2 = g.next(res1);
console.log('g2:', g2); // g2: { value: Promise { 222 }, done: false }
g2.value
.then(res2 => {
console.log('res2:', res2); // res2: 222
g.next(res2);
})
.catch(err2 => {
console.log(err2);
});
})
.catch(err1 => {
console.log(err1);
});
以上代码是 Generator 和 callback 结合实现的异步,可以看到,仍然需要手动执行 .then 层层添加回调,但由于 next() 方法返回对象 {value: xxx,done: true/false} 所以我们可以简化它,写一个自动执行器:
const run= gen => {
const g = gen();
const next = data => {
const res = g.next(data);
// 深度递归,只要 `Generator` 函数还没执行到最后一步,`next` 函数就调用自身
if (res.done) return res.value;
res.value.then(function(data) {
next(data);
});
}
next();
}
run(function*() {
const res1 = yield new Promise((resolve)=>resolve(111))
console.log(res1); // 111
const res2 = yield new Promise((resolve)=>resolve(222))
console.log(res2); // 222
});
(3)下面来看看Async await的原理和实现:
首先对比一下Generator、async/await的写法,是不是很相似呢?
// Generator
run(function*() {
const res1 = yield new Promise((resolve)=>resolve(111));
console.log(res1);
const res2 = yield new Promise((resolve)=>resolve(222));
console.log(res2);
});
// async/await
const readFile = async ()=>{
const res1 = await new Promise((resolve)=>resolve(111));
console.log(res1);
const res2 = await new Promise((resolve)=>resolve(222));
console.log(res2);
return 'done';
}
可以看到,async function
其实是代替了 function*
,await
代替了 yield
,同时也无需自己手写一个自动执行器 run 了。
async/await 有以下的特点:
- 当
await
后面跟的是Promise
对象时,才会异步执行,其它类型的数据会同步执行 - 执行
const res = readFile();
返回的仍然是个Promise
对象,上面代码中的return 'done';
会直接被下面then
函数接收到