我觉得脱离promise去谈async是不全面的,理解promise对学习和使用async具有很大的意义。直接看一个阮一峰上Promise对象实现Ajax操作的实例,看着实例去解析:
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
promise是个对象实例,它有3种状态,pending(进行中)、resolved(已成功)、rejected(已失败),这个对象接受一个带有2个函数参数的函数参数a,这两个函数参数是resolve和reject,在a中调用它们就可以改变这个promise对象的状态,我们看到例子中根据请求的返回状态来决定是调用resolve还是reject,promise的状态确定了之后就是不可逆的了,之后的操作就交给了then
函数,then
接受两个函数对象,分别来处理resolved状态和rejected状态的情况。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
为了强化理解,我们在看一个阮的例子:
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
这里我们可以看到两个promise对象,分别在所有脚本执行完后的1s和3s改变状态,p1作为p2的resolve参数,也就是p2需要等待p1的状态发生改变,1s之后p2就会执行resolve,返回p1,p1的状态还没确定,所以就需要等p1的状态确定,2s后p1返回rejected状态,触发catch。
一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
catch()也是一个重要的对象方法,一段健壮的代码需要有捕获错误的处理逻辑,catch方法其实也就是.then(null, rejection)
的别名,如果promise对象状态变为rejected,就会调用catch方法指定的回调函数,
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
reject其实就是等价于抛出错误。catch会处理之前所有promise产生的错误,所以我们就不需要在then方法中在定义rejected状态的回调函数,在最后使用catch方法就可以了:
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) {
//cb
// success
})
.catch(function(err) {
// error
});
上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。
再回到之前的实例,js自带的ajax请求已经比较少用了,我们采用fetch的方式再实现一下(fetch就是基于promise实现的,具体用法参考MDN-fetch的使用):
const getJSON = function(url) {
return new Promise(function(resolve,reject){
fetch(serverUrl)
.then(response => response.json())
.then(json => resolve(json))
.catch(ex => reject(ex))
});
};
getJSON("/posts.json").then(json=>{
console.log('Contents: ' + json);
}).catch(error=>{
console.error('出错了', error);
});
这就是promise的一个简单用法,到这边都理解了,之后的all,race什么的拓展,即看即用就可以了。
有时候非常痛恨这样一种感觉,当你想要了解一个概念,发现就需要去了解另一个概念,要了解另一个概念,还得追溯其它的知识,最后等你真正理解了,发现看的东西都差不多占了半本书了,好处就是无形之中学了很多东西,坏处就是达成目的所耗费的时间大大增加,实在令人抓狂,当初了解promise的时候都是从异步,回调,并发这些东西一个个看起的,同样,了解async,还需要除了这些之外的generator,,虽然async,await是generator的一个语法糖,但是我觉得要去使用async,并不一定需要了解generator,两者在使用上可以割裂开来,所以我就不花篇幅写这个了。。。
async是我们定义函数时的一个修饰词,当用async定义函数时,表示我们的函数体内有些操作不是立即能执行完的–异步的,就比如异步请求:
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});
getStockSymbol和getStockPrice就是async函数中的一部操作,then中需要执行的操作就需要等到这两个异步请求都完成并且返回stockPrice这个promise对象后才可以执行。
如果await返回的是rejected,那么then就没办法执行了,所以我们需要用try catch 包裹await语句,当然最好还是用一下方式:
async function myFunction(){
await somethingThatReturnAPromise().catch(function (err){
console.log(err);
})
}
面对多个请求并发的情况,同样可以使用Promise.all来处理:
async function dbFunction(db){
let docs = [{},{},{}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
最后,我们不妨用async的方式把最初的函数改造一下,我们把最终的处理请求结果的函数封装起来:
const getJSON = function(url) {
return new Promise(function(resolve,reject){
fetch(serverUrl)
.then(response => response.json())
.then(json => resolve(json))
.catch(ex => reject(ex))
});
};
getJSON("/posts.json").then(json=>{
console.log('Contents: ' + json);
}).catch(error=>{
console.error('出错了', error);
});
/***************************async******************************/
async getResultData(url)=>{
const resultData = await getJSON(url).catch((err)=>{
console.log(err)
})
return resultData;
}
getResultData('/posts.json').then((resultData)=>{
console.log('Content:' +json)
})
直观当然是直观一些,但是不知道我这种写法是不是写烦了,还是可以直接写成下面的形式:
async getResultData(url)=>{
const resultData = await getJSON(url);
return resultData;
}
getResultData('/posts.json').then((resultData)=>{
console.log('Content:' +json)
}).catch((err)=>{
console.log(err)
})
欢迎指正。