Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
Generator 函数是一个普通函数,但是有两个特征。一是关键字星号(function *
);二是,函数体内部使用yield表达式,定义不同的内部状态。执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hello = helloWorldGenerator();
上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。但是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(Iterator对象)
调用generator对象有两个方法:
next()
方法,next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值,也表示这个generator对象就已经全部执行完毕,不要再继续调用next()了。hello .next() // { value: 'hello', done: false }
hello .next() // { value: 'world', done: false }
hello .next() // { value: 'ending', done: true }
hello .next() // { value: undefined, done: true }
for(var i of hello){
console.log(i)
}
// hello
// world
async/await实际上是对Generator(生成器)的封装,是一个语法糖。
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。async函数自带执行器,自动执行,无需next()
async function helloAsync() {
return "hello async";
}
const result = helloAsync();
console.log(result); // Promise { 'hello async' }
async
函数的返回值是 Promise 对象,可以用then方法指定下一步的操作。async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
await
是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。
– 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
– 如果它等到的是一个 Promise 对象,await 会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
Promise对象有以下两个特点。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise
实例具有then
方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
// setTimeout 来模拟异步操作
function setTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function fn1(n) {
return takeLongTime(n);
}
function fn2(n) {
return takeLongTime(n);
}
function fn3(n) {
return takeLongTime(n);
}
function doneFn() {
const time1 = 300;
step1(time1) // time1: 300
.then(time2 => step2(time2)) // time2: 500
.then(time3 => step3(time3)) // time3: 700
.then(time4 => { // time4: 900
// do something...
});
}
doneFn();
Generator、async/await、Promise均可以用来实现异步处理。
1、async函数的返回值是 Promise 对象,且async函数自带执行器,自动执行,无需next。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
2、Generator 函数的返回值是 Iterator对象,可以通过next或 for of遍历执行。
3、Promise 状态更加清晰,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。Promise.then方法更加逻辑清晰,且有promise.all/promise.race都接口提供
缺点:
1、async/await 错误处理机制问题,await 的值可能报错,最好放在try catch中执行。
2、await命令只能用在async函数之中,如果用在普通函数,就会报错。
3、async 函数可以保留运行堆栈 ,暂时保存当前执行栈
4、Promise 无法取消,一旦新建它就会立即执行,无法中途取消。