那次他们约莫说了呵呵 Promise ,那时就接著讲 async/await 这组 API 。
async/await 是 ES7 的国际标准,Promise 是 ES6 国际标准,async/await 这套 API 也是用以协助他们写触发器标识符的,它是构筑在 Promise 其内的,类似于 Okhttp 和 Retrofit 的亲密关系。
甚么是 async ?
async function myFirstAsyncFunction() { try { const fulfilledValue = await doSomeThing(); } catch (rejectedValue) { // … } }约莫长下面这个模样。async 通常不原则上采用,而要和 await 一同采用,两个 async 表达式外部可能将有 开始符号 或是 数个 await 。
这段标识符即便没段小宇 Promise 也很难看懂。这是 async 表达式的竞争优势所处。
async 表达式被初始化的这时候,会立刻回到两个 Promise。
await
await 无法原则上采用,假如在非 async 表达式外部被初始化会收起。await 前面通常跟两个 Promise ,也能是其它的,比如说两个值,或是两个表达式,或是两个表达式。假如 await 前面并非两个 Promise 就会回到两个早已 resolve 的 Promise。
当 async 表达式继续执行到 await 的这时候,会中止整座async表达式的继续执行民主化并卖地其控股权,多于宇内等候的如前所述Promise 的触发器操作方式被落空或被婉拒后才会恢复正常民主化。
总之,async 表达式也会回到两个 Promise ,也就是说,await 前面也能跟两个 async 表达式。
async/await 这套 API ,感觉有 Kotlin 中的 协程 和 挂起表达式 内味。
为甚么要采用 async ?
隐藏 Promise ,更易于理解
假设他们想请求两个接口,然后把响应的数据打印出来,并且捕获异常。用 Promise 约莫是这样写:
function logFetch(url) { returnfetch(url) .then(response => response.text()) .then(text => { console.log(text); }).catch(err => { console.error(fetch failed, err); }); }假如用 async 表达式来写,约莫是那个模样:
async function logFetch(url) { try { constresponse =await fetch(url); console.log(await response.text()); } catch (err) { console.log(fetch failed, err); } }虽然标识符的行数差不多,但是标识符看起来更加简洁,少了很多 then 的嵌套。请求两个接口数据,然后打印,就像你看到的,很简单。
就像刚才的那个例子,假如没段小宇 Promise 也不影响你看懂和理解 async 标识符。那个好像没甚么,但是对于两个大型的项目,人员的技术水平参差不齐,无法保证所有人都熟悉和理解 Promise ,他们要做的是尽可能将的降低标识符的理解难度,使大部分人都能看懂。
用同步的思路写触发器逻辑
async/await 最大的竞争优势是他们能用同步的思路来写触发器的业务逻辑,所以标识符整体看起来更加难看懂。他们举个例子。
下面的标识符还是比较简单,他们再举两个复杂一点的例子。
function getResponseSize(url) { return fetch(url).then(response=> { const reader = response.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); }) }); }即便你段小宇 Promise ,那个标识符也并并非很好理解,更别说是没段小宇 Promise 的同学了。因为中间有两个循环的过程,而且那个继续执行的过程的触发器的,并不想他们之前学到的两个链式初始化能解决的。总之,你也能抽取呵呵,使标识符更简洁一点。
const processResult = (result) =>{ if (result.done) return total; constvalue = result.value; total += value.length;console.log(Received chunk, value); return reader.read().then(processResult); } function getResponseSize(url) { return fetch(url).then(response => { const reader = response.body.getReader(); let total = 0; return reader.read().then(processResult) }); }但是多了两个方法,勉勉强强吧。但是他们看呵呵 async 表达式是怎么处理的。
async function getResponseSize(url) { const response = await fetch(url); constreader = response.body.getReader();let result = await reader.read(); let total = 0; while (!result.done) { const value = result.value; total +=value.length; console.log(Received chunk, value); // get the next result result = awaitreader.read(); }return total; }OHHHHHHHHH
那个是并非看起来就更加流畅了?因为 await 表达式会阻塞运行,甚至能直接阻塞循环,所以整体看起来像同步的标识符,也更符合直觉,更难读懂那个标识符。
小心 await 阻塞
由于 await 能够阻塞 async 表达式的运行,所以标识符看起来更像同步的标识符,更难阅读和理解。但是要小心 await 阻塞,因为有些阻塞是不必要的,不恰当采用可能将会影响标识符的性能。
假如他们要把两个网络数据和本地数据合并,错误的实例可能将是这模样:
async function combineData(url, file) { letnetworkData =await fetch(url) let fileData = await readeFile(file) console.log(networkData + fileData) }其实他们不用等两个文件读完了,再去读下个文件,他们能两个文件一同读,读完后再进行合并,这样能提高标识符的运行速度。他们能这样写:
async function combineData(url, file) { let fetchPromise = fetch(url) let readFilePromise = readFile(file) let networkData = await fetchPromise letfileData =await readFilePromise console.log(networkData + fileData) }这样的话,就能同时 网络请求 和 读取文件 了,能节省很多时间。这里主要是利用了 Promise 一旦创建就立刻继续执行的特点,不懂的同学能复习呵呵上一节的内容。
总之,假如你熟悉 Promise 的话,能直接采用 Promise.all 的方式来处理,或是 await 前面跟 Promise.all 这里就不展开讲了。
异常处理
try…catch
在 async 表达式中,异常处理通常是 try…catch ,假如没进行 try…catch ,await 表达式一旦 reject ,async 表达式回到的 Promise 就会 reject 。
其实结合 Promise 来看,假如两个 Promise 状态敲定为 reject ,并且后续的 then 没传入 reject 表达式,或是没 catch ,那么就会抛出异常。从那个角度来看,在 async 表达式中用 try…catch 来包住 await 表达式,可能将是 catch 住那个异常,并且把那个 reject 信息传到 catch 里面。
这里就不举例子了。