异步
- 不连续的执行,就叫做异步。相应地,连续的执行就叫做同步。
- 通常异步是处理一些耗时的操作。
回想在ES6没出现之前,需要异步处理事件的时候大多数情况下就是回调函数,可能还是是一层一层的callback嵌套,代码混乱逻辑不清晰,如果还有错误处理,那代码就更加冗杂让人不想去读第二遍!
回调
通过函数参数传递到其他代码里的,某一块可以执行的引用。
容易引发的问题
1.信任问题
2.同步or异步 不确定?
Promise对象
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
- Promise 是一个对象,从它可以获取异步操作的消息。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
构建Promise对象
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
}
else {
reject(error);
}
});
promise的构造函数传入一个回调,该回调接受两个回调函数resolve和reject。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise 提供then方法加载回调函数,catch方法捕捉执行过程中抛出的错误。
Promise.resovle方法,可以将不是Promise对象作为参数,返回一个Promise对象。
有两种情形:
1.假设传入的参数没有一个.then方法,那么这个返回的Promise对象变成了resolve状态,其resolve的值就是这个对象本身。
2.假设传入的参数带有一个then方法(称为thenable对象), 那么将这个对象的类型变为Promise,其then方法变成Promise.prototype.then方法。
不管是then还是catch方法调用,都返回一个新的promise对象;其实then方法的调用,只不过是注册了完成态和失败态下的回调函数而已。这些回调函数组成一个回调队列,处理resolve的值。
Q 链式调用的then和非链式调用的有什么区别?
var promise1 = new Promise(function(resolve){
resolve(1);
});
var thenPromise = promise1.then(function(value){
console.log(value);
});
var catchPromise = thenPromise.catch(function(error){
console.log(error);
});
console.log(promise1 !== thenPromise); // true
console.log(thenPromise !== catchPromise); //true
情况1
var promise1 = new Promise(function(resolve){
resolve(1);
});
promise1.then(function(value){
return value * 2;
});
promise1.then(function(value){
return value * 2;
});
promise1.then(function(value){
console.log("1"+value);
});
情况2
var promise1 = new Promise(function(resolve){
resolve(2);
});
promise1.then(function(value){
return value * 2;
}).then(function(value){
return value * 2;
}).then(function(value){
console.log("1"+value);
});
Generator 函数
整个 Generator 函数就是一个异步任务的容器。
Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。每次调用遍历器对象的next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止,会返回一个有着value和done两个属性的对象,value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
function* gen(x) {
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
- Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
- Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。
- 除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。
function* asyncJob(){
// ...其他代码
varf=yieldreadFile(fileA);
// ...其他代码
}
上面代码的函数asyncJob是一个协程,它的奥妙就在其中的yield命令。它表示执行到此处,执行权将交给其他协程。
协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。
它的最大优点,就是代码的写法非常像同步操作。
async函数
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await
async告诉这个函数里面有await
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
- async函数返回一个 Promise 对象。
- async函数内部return语句返回的值,会成为then方法回调函数的参数。async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
- 如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。抛出的错误对象会被catch方法回调函数接收到。
await拿到的东西就是promise resolve之后的值
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
function spawn(genF) {
return new Promise(function(resolve, reject) {
var gen = genF();
function step(nextF) {
try {
var 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); });
});
}
比较
一个异步展示动画的程序
// Promise写法
function chainAnimationPromise(elem, animations) {
var ret = null;
var p = Promise.resolve();
for (var anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
return p.catch(function(e) {
}).then(function() {
return ret;
});
}
// Generator写法
function chainAnimationPromise(elem, animations) {
return spawn(function* () {
var ret = null;
try {
for (var anim of animations) {
ret = yield anim(elem);
}
} catch (error) {
}
return ret;
});
}
// async写法
async function chainAnimationPromise(elem, animations) {
var ret = null;
try {
for(var anim of animations) {
ret = await anim(elem);
}
} catch (error) {
}
return ret;
}