简单介绍下这几个的关系
为方便起见 用以下代码为例简单介绍下这几个东西的关系,
async function buildData(name) {
try {
let response1 = await axios.get('/api/user?name=' + name);
let userInfo = response1.data;
let response2 = await axios.get('/api/topics?user_id' + userInfo._id);
let posts = response2.data;
// i got it.
} catch(err) {
console.log(err);
}
}
buildData('xiaoming');
在函数声明前使用async关键词修饰 说明函数中有异步操作
等待 后面的代码执行完毕 再继续向下执行
Promise 是一个对象,从它可以获取异步操作的消息,知道异步函数是完成了还是出错了。
axios返回的结果就是一个promise
try catch JavaScript的异常捕获机制,凡是在try语句块中的代码出错了,都会被catch捕获。
上面的代码就是说
上面说的太过简单,简要的说明下 各个东西是干啥的。这里的核心是 promise 下面会逐个介绍
Promise字面上讲,是一个承诺。这个承诺有三个状态
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点:
只要这两种情况发生,状态就凝固了,不会再改变了,会一直保持这个结果,这时就定型了 resolved。
如果改变已经发生了,任何时候添加回调函数,得到的都是这个结果。
因为创建Promise对象时,回调函数中有resolve和reject两个参数,后续的resolved统一指的是fulfilled状态,不包括rejected状态
一个Promise对象一旦状态确定了,它的使命也就结束了。后面的代码都不应该再执行了,最好return resolve();
如果状态已经resolve了,再在后面抛出错误也是无效的,也不会改变状态为rejected,后面有异常也不会抛出。
Promise对象如何知道异步操作的结果呢,那就是回调函数了,一个表示成功resolve,一个表示失败reject,
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
下面代码创造了一个Promise实例。
var promise = new Promise(function(resolve, reject) {
// ... some code
console.log(我一创建就执行了)
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise新建后,就会立即执行,返回一个Promise对象。
Promise构造函数需要一个函数作为参数,这个函数有两个参数,分别是resolve和reject,
它们是两个函数,由 JavaScript 引擎提供,不用自己部署
所以:
通过then方法获取异步操作的结果。
Promise实例有两个方法:
then和catch方法返回的是一个新的Promise对象,因为Promise对象具有then和catch方法,所以可以一直.then和.catch下去
axios返回的也是Promise对象,这也是为什么axios可以那么链式操作了
then方法的作用就是干这个
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法的参数是两个回调函数,都接受Promise对象传出的值作为参数:
通常这个参数也不写,因为Promise实例还有一个方法叫catch是专门用来捕获异常的。
catch
catch 是 .then(null, rejection)的别名,用于指定发生错误时的回调函数。
一旦catch前面的任何一个Promise发生异常,都会被catch捕获,包括Promise函数创建的Promise,还有.then返回的Promise,甚至catch前面如果还有一个catch在这个catch抛出的异常也会被后一个catch捕获。
也就是说:
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止,也即是说,错误总会被下一个catch语句捕获。
所以,既然这个catch这么厉害,then函数中的第二个参数常常被省略了,然后被这个catch方法替代。
所以通常这么写:
promise.then().catch()
promise.then().then().catch()
promise.then().then().catch().then().catch()
所以下面例子第二种写法好些。
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法和catch方法。
Promise.resolve方法就起到这个作用
Promise.resolve('foo')
// 等价于
nnew Promise(function (resolve) {
resolve('foo')
})
Promise.resolve方法的参数分成四种情况:
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
上面代码中,setTimeout(fn,0),在下一轮循环事件开始执行
Promise.resolve()在本轮事件循环结束时执行
console.log('one')立刻执行,
因此上面的打印顺序是 one two three
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
var p = Promise.reject('出错了');
//等价于
new Promise(function (resolve,reject) {
reject('出错了');
})
Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。不会像Promise.resolve那样,根据不同的情况包装Promise
async函数是Generator函数的语法糖,将Generator的星号换成async 将yield换成await
Generator是一个状态机,封装了多个内部状态,执行 Generator 函数会返回一个遍历器对象,返回的遍历器对象,可以使用next依次遍历 Generator 函数内部的每一个状态。Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。Generator 函数的执行必须靠执行器。
async函数比Generator函数更好用
正常情况下,await命令是个Promise对象,如果不是 会被转成一个 立即 resolved的对象
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象(因为await 函数返回的是Promise对象),而await命令就是内部then命令的语法糖。
然而,然而,我们没写错误处理。
async function f() {
return 'hello world';
}
f().then().catch()
正常情况下 async 函数中return结果会使Promise对象变为 resolved状态,返回值作为then方法回调函数的参数,而出错则会使Promise对象的变为reject状态,错误会被catch捕获。
因为 async函数 相当于对 多个Promise的封装,所以必须等到内部所有的await命令执行完,才会改变自己的状态为resolved,除非 碰到return语句或者抛出了异常。
也就是说,正常情况下 只有async函数内部的异步操作执行完,才会执行then后面的语句。
只要一个await后面的Promise变为rejected,整个async函数就会中断执行,整个async返回的Promise对象就会是rejected状态
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
因为第一个await后面的对象reject了,所以整个async函数就中断执行了
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。
这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。
try catch是JavaScript的异常处理机制,把可能出错的代码放在try语句块中,如果出错了,就会被catch捕获来处理异常。如果不catch 一旦出错就会造成程序崩溃。
如果有多个await命令,可以将其都放在try catch结构中,如果执行出错,catch会去捕获异常
async function f() {
try {
await Promise.reject('出错了');
console.log('上面已经出错了');
return await Promise.resolve('hello world');
} catch(e) {
console.log(e);
}
}
f()
.then(v => console.log(v))
catch会去捕获try代码块中的错误,只要有一个抛出了异常,就不会继续执行,所以上面的代码不会打印上面已经出错了也不会执行return await Promise.resolve('hello world');
因为使用了trycatch 所以 async 是顺利执行完成的,其中的报错 被 try catch处理了,所以异常不会被async返回的Promise的catch捕获,因此async返回的Promise对象状态是resolved。
await 会等待后面的异步操作执行完毕,才会继续执行
let foo = await getFoo();
let bar = await getBar();
上面的代码会顺序执行,
如果需要多个await没有相互依赖,最好让他们同时触发,可以使用以下两种方式:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
//没用await 立即执行返回 Promise对象
let fooPromise = getFoo();
let barPromise = getBar();
// 等待 Promise对象的结果 之前也说过 await就像是then的语法糖
let foo = await fooPromise;
let bar = await barPromise;
作者:_我和你一样
链接:https://www.jianshu.com/p/2037e3b53d5f
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。