利用异步函数async,可以像写同步代码那样写基于Promise的代码,还可以让异步代码主线程.
await某个Promise时,函数暂停执行,直到该Promise产生结果,并且暂停并不会重启主线程.
async的工作方式:
async function myFirstAsyncFunction(){
try {
const fulfilledValue = await promise;
}
catch (rejectedValue) {
// ...
}
}
示例: 通过fetch获取网络日志
之前:
function logFetch(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
console.log(text);
}).catch(err => {
console.error('fetch failed', err)
});
}
使用async之后:
async function logFetch(url){
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.error('fetch failed', err)
}
}
使用 async 相较 promise,去掉了所有回调.
示例: 流式传输响应
在流式传输响应的同时,记录数据块日志,并返回数据块最终大小.
之前:
function getResponseSize(url){
return fetch(url).then(response => {
const reader = reponse.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
})
});
}
这是一段精巧的代码,在 processResult 内部调用其自身来建立异步循环,看起来很只能,但是得盯着看上一会儿才能明白其作用.
使用async之后:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
tatol += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
让人大有飘飘然的异步循环被替换成可靠却单调乏味的while循环,但简明性大幅提升.
未来,将使用异步迭代器
来将 while 循环替换成 for-of 循环,从而进一步提高代码简明性.
其它使用async的场景,在剪头函数中,对象方法中,类方法中.类构造函数以及getter/setter方法中不能是异步的.
弊端写法,过于循序.示例: 获取一系列网址并尽快按照正确顺序将其记录到日志中
[不推荐]promise写法
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map(url => {
return fetch(url).then(response => response.text());
});
// log them in order
textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
上面还使用了智能的reduce来对Promise序列进行链接,但这种编码过于"智能"还是不要为好.
使用async简单改写以上代码会让代码变得过于循序.
[不推荐]async的过于循序写法
async function logInOrder(urls) {
for (const url of urls) {
const response = await fetch(url);
console.log(await response.text());
}
}
代码简洁不少,但我们想要的第二次获取必须等到第一次获取完毕之后才能开始,执行效率比并行执行获取的Promise低得多.
进一步使用async改写,使其并行效率高:
async function logInOrder(urls) {
// fetch all the URLs in parallel
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// log them in sequence
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
以并行方式获取和读取网址,但将"智能的"reduce替换成了标准的单调的乏味的但可读性强的for循环.
使用generator生成器进行polyfill
原先异步代码:
async function slowEcho(val) {
await wait(1000);
return val;
}
进行polyfill:
const slowEcho = createAsyncFunction(function*(val){
yield wait(1000);
return val;
})
将生成器(function*)传给createAsyncFuntion,并使用yield来代替await.
babel里polyfill await的工作方式
简单async代码:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.log('fetch failed', err);
}
}
转换后:
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function logFetch(_x) {
return _logFetch.apply(this, arguments);
}
function _logFetch() {
_logFetch = _asyncToGenerator(function* (url) {
try {
const response = yield fetch(url);
console.log(yield response.text());
} catch (err) {
console.log('fetch failed', err);
}
});
return _logFetch.apply(this, arguments);
}