咱们常常会在编程时遇到异步的需求,
异步
,就是说一个任务不是能够连续完成,他须要被分红前后执行,先执行一部分,而后程序能够转执行别的任务,待执行完了前一部分,在回头执行后一部分,那么咱们曾经使用的哪些方法实现过呢,这里咱们介绍一下:es6
1.回调函数
2.Promise
3.async await
复制代码
举一个例子:咱们对一个文件的读取和修改操做编程
fs.readFile('./ziyi.text', (content) => {
setTimeout(() => {
content += '123';
fs.append('./ziyi.text', content, (err) => {
......
})
}, 3000)
})
复制代码
这里咱们经过回调函数,能够在读取文件三秒后,进行文件内容的修改,在回调里面又有回调实现异步,可是这就陷入了回调地狱
,因此咱们后面异步的实现就出现了链式调用的Promise
app
Promise('./ziyi.text')
.then(() => {
content += '123';
})
.then(() => {
fs.append('./ziyi.text')
})
.then()
......
复制代码
这样链式调用看上去咱们的代码就像串联同样,再也不是多个回调函数嵌套时,Promise虽然跳出了异步嵌套的怪圈,用链式表达更加清晰,可是咱们也发现若是有大量的异步请求的时候,流程复杂的状况下,会发现充满了屏幕的then,看起来很是吃力,而ES7的Async/Await
的出现就是为了解决这种复杂的状况。。异步
async () => {
let c = await fs.readFile('./ziyi.text')
c += '123';
let res = await fs.append('./ziyi.text', c);
}
复制代码
这样咱们的代码阅读起来就很清晰了,每一步作的事都简介明了,其实,在Promise到async以前,做者TJ
写了一的Co
的库优化了Promise写法,这个优化使用了es6里Generator
,大概的写法以下:yield
表示暂停,看上去是否是和咱们如今用的async await很像async
co(
function * test() {
let c = yield fs.readFile('./ziyi.text');
c += '123';
let res = yield fs.append('./ziyi.text', c);
}
)
复制代码
generator
(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但能够返回屡次。函数
以前的Promise恢复了异步回调的可信任性,而generator正是以一种看似顺序、同步的方式实现了异步控制流程,加强了代码可读性。优化
generator和函数不一样的是,generator由function*定义,而且,除了return语句,还能够用yield返回屡次。它的执行由next()方法来一步一步执行ui
举个例子:spa
function foo() {
// setTimeout(() => {
// console.log(1);
// }, 2000)
// return 'foo';
return new Promise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000)
})
}
function* test() { //generator
console.log('start');
let a = yield foo(); // 赋值语句从右往左<——,因此执行到foo()会中止
console.log('middle')
let b = yield 2;
console.log('end');
}
let g = test();
console.log(g)
console.log(g.next()); //执行第一部分,代码开始到第一个yield
复制代码
let g = test();
咱们先把text()执行结果拿到,在它上面由next()方法执行generator里的代码
g.next()
经过第一次调用next,咱们执行到了foo(),g.next()返回的对象key由value和done,value为yield 后面的内容,done表示是否还有next能够执行,这里done为false,则generator未执行完3d
function* test() {
console.log('start');
let a = yield foo();
console.log('a', a);
let b = yield 2;
console.log('b', b);
console.log('end');
}
let g = test();
console.log(g)
console.log(g.next());
console.log(g.next());
console.log(g.next());
复制代码
可是咱们的a,b 未被赋到值,generator规定在next()
方法传参能够给上一个(注意是上一个)yield返回值,代码以下
console.log(g.next());
console.log(g.next('A_value'));
console.log(g.next('B_value'));
复制代码
console.log(g.next());
console.log(g.next(foo()));
console.log(g.next(2));
复制代码
function generateAutoRun(gen) {
let g = gen();
function next(value) {
let res = g.next(value); //执行next()
if (res.done) return; //结束
next(res.value); //从第二个next开始,给上一个的yield返回值
}
next();
}
generateAutoRun(test);
复制代码
value
值给yield的返回,这样a就返回了foo()执行的结果,b就为2。
介绍完了generator,咱们就可能够用它来实现一下咱们async了
咱们定义两个异步的 Promise
前后执行
let p1 = new Promise((resolve) => {
console.log('p1先');
setTimeout(() => {
console.log(1);
resolve(1)
}, 2000)
})
let p2 = new Promise((resolve) => {
console.log('p2后');
setTimeout(() => {
console.log(2);
resolve(2)
}, 1000)
})
function* test() {
let a = yield p1;
console.log(a,'a---')
let b = yield p2;
console.log(b,('b---'));
}
复制代码
这时咱们就要实现p1,p2的前后执行了
function asyncTogenerate(gen) {
let g = gen();
function step(value) { //递归调用next
// 处理 yield 返回值问题
let info = g.next(value); //一步一步执行p1,p2,并给a,b 赋值p1,p2
if (info.done) { //执行完return掉
return;
} else {
// 把 yield 后面的东西(info.value) 直接 resolve,传给下一个step,给a,b赋值
Promise.resolve(info.value).then((res) => {
// 下一个 yield 下一个 递归
step(res);
})
}
}
step(); //递归开始
}
asyncTogenerate(test);
复制代码
Promise的then
方法,能够保证前后执行,因此咱们上面代码给每个yield返回的值进行Promise包装一下
Promise.resolve(info.value).then((res) => { //把value包装,使用它的then方法异步执行
复制代码
看结果
正如咱们想要的同样,p1先执行,而后再是p2,输出结果也没问题,由于p1是1秒后执行,p2是2秒后执行,因此先输出2,再输出1。
a为p1返回的1,,b为p2返回的2,赋值也没有错误。
咱们常说什么async/await
的出现淘汰了Promise
,能够说是大错特错,偏偏相反,上面的例子说明,正由于有了Promise,才有了改良版的async/await,二者是能够相辅相成的
因此咱们想学好async/await,应该先去了解 Promise