摘要:基礎(chǔ)從中的說起上和在異步操作中使用和是一件比較費(fèi)勁的事情,而給我們提供了更為簡便的和。表達(dá)式會(huì)暫停當(dāng)前的執(zhí)行,等待處理完成。若正常處理,其回調(diào)的函數(shù)參數(shù)作為表達(dá)式的值,繼續(xù)執(zhí)行。若處理異常,表達(dá)式會(huì)把的異常原因拋出。
寫在前面
本文首發(fā)于公眾號:【符合預(yù)期的CoyPan】
在上一篇文章中,梳理了javascript中的兩個(gè)重要概念:iterator和generator,并且介紹了兩者在異步操作中的應(yīng)用。
【JS基礎(chǔ)】從JavaScript中的for...of說起(上) - iterator 和 generator
在異步操作中使用iterator和generator是一件比較費(fèi)勁的事情,而ES2017給我們提供了更為簡便的async和await。
async和await asyncmdn上說:async function 聲明用于定義一個(gè)返回 AsyncFunction 對象的異步函數(shù)。異步函數(shù)是指通過事件循環(huán)異步執(zhí)行的函數(shù),它會(huì)通過一個(gè)隱式的 Promise 返回其結(jié)果。
簡單來說,如果你在一個(gè)函數(shù)前面使用了async關(guān)鍵字,那么這個(gè)函數(shù)就會(huì)返回一個(gè)promise。如果你返回的不是一個(gè)promise,JavaScript也會(huì)自動(dòng)把這個(gè)值"包裝"成Promise的resolve值。例如:
// 返回一個(gè)promise async function aa() { return new Promise(resolve => { setTimeout(function(){ resolve("aaaaaa"); }, 1000); }); } aa().then(res => { console.log(res); // 1s后輸出 "aaaaaa" }); typeof aa === "function"; // true Object.prototype.toString(aa) === "[object AsyncFunction]"; // true Object.prototype.toString(aa()) === "[object Promise]"; // true // 返回一個(gè)非promise async function a() { return 1; } const b = a(); console.log(b); // Promise?{: 1} a().then(res => { console.log(res); // 1 })
當(dāng) async 函數(shù)拋出異常時(shí),Promise 的 reject 方法也會(huì)傳遞這個(gè)異常值。例如下面的例子:
async function a(){ return bbb; } a() .then(res => { console.log(res); }) .catch( e => { console.log(e); // ReferenceError: bbb is not defined });await
await 操作符用于等待一個(gè)Promise 對象。它只能在異步函數(shù) async function 中使用。await 表達(dá)式會(huì)暫停當(dāng)前 async function 的執(zhí)行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回調(diào)的resolve函數(shù)參數(shù)作為 await 表達(dá)式的值,繼續(xù)執(zhí)行 async function。若 Promise 處理異常(rejected),await 表達(dá)式會(huì)把 Promise 的異常原因拋出。另外,如果 await 操作符后的表達(dá)式的值不是一個(gè) Promise,則返回該值本身??聪旅娴睦樱?/p>
const p = function() { return new Promise(resolve => { setTimeout(function(){ resolve(1); }, 1000); }); }; const fn = async function() { const res = await p(); console.log(res); const res2 = await 2; console.log(res2); }; fn(); // 1s后,會(huì)輸出1, 緊接著,會(huì)輸出2 // 把a(bǔ)wait放在try catch中捕獲錯(cuò)誤 const p2 = function() { return new Promise(resolve => { console.log(ppp); resolve(); }); }; const fn2 = async function() { try { await p2(); } catch (e) { console.log(e); // ppp is not defined } }; fn2();
當(dāng)代碼執(zhí)行到await語句時(shí),會(huì)暫停執(zhí)行,直到await后面的promise正常處理。這和我們之前講到的generator一樣,可以讓代碼在某個(gè)地方中斷。只不過,在generator中,我們需要手動(dòng)寫代碼去執(zhí)行g(shù)enerator,而await則是像一個(gè)自帶執(zhí)行器的generator。某種程度上,我們可以理解為:await就是generator的語法糖??聪旅娴拇a:
const p = function() { return new Promise(resolve, reject=>{ setTimeout(function(){ resolve(1); }, 1000); }); }; const f = async function() { const res = await p(); console.log(res); }
我們使用babel對這段代碼進(jìn)行轉(zhuǎn)化,得到以下的代碼:
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } var p = function p() { return new Promise(resolve, function (reject) { setTimeout(function () { resolve(1); }, 1000); }); }; var f = function () { var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var res; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return p(); case 2: res = _context.sent; console.log(res); case 4: case "end": return _context.stop(); } } }, _callee, this); })); return function f() { return _ref.apply(this, arguments); }; }();
通過變量名可以看到,babel也是將async await轉(zhuǎn)換成了generator來進(jìn)行處理的。
任務(wù)隊(duì)列以下的場景其實(shí)是很常見的:
我們有一堆任務(wù),我們需要按照一定的順序執(zhí)行這一堆任務(wù),拿到最終的結(jié)果。這里,把這一堆任務(wù)稱為一個(gè)任務(wù)隊(duì)列。
js中的隊(duì)列其實(shí)就是一個(gè)數(shù)組。
同步任務(wù)隊(duì)列任務(wù)隊(duì)列中的函數(shù)都是同步函數(shù)。這種情況比較簡單,我們可以采用reduce很方便的遍歷。
const fn1 = function(i) { return i + 1; }; const fn2 = function(i) { return i * 2; }; const fn3 = function(i) { return i * 100; }; const taskList = [fn1, fn2, fn3]; let a = 1; const res = taskList.reduce((sum, fn) => { sum = fn(sum); return sum; }, a); console.log(res); // 400異步任務(wù)隊(duì)列
任務(wù)隊(duì)列中的函數(shù)都是異步函數(shù)。這里,我們假設(shè)所有的函數(shù)都是以Promise的形式封裝的?,F(xiàn)在,需要依次執(zhí)行隊(duì)列中的函數(shù)。假設(shè)異步任務(wù)隊(duì)列如下:
const fn1 = function() { return new Promise( resolve => { setTimeout(function(){ console.log("fn1"); resolve(); }, 2000); }); }; const fn2 = function() { return new Promise( resolve => { setTimeout(function(){ console.log("fn2"); resolve(); }, 1000); }); }; const fn3 = function() { console.log("fn3"); return Promise.resolve(1); }; const taskList = [fn1, fn2, fn3];
可以使用正常的for循環(huán)或者for...of... 來遍歷數(shù)組,并且使用async await來執(zhí)行代碼(注:不要使用forEach,forEach不支持這種場景)
// for循環(huán) (async function(){ for(let i = 0; i < taskList.length; i++) { await taskList[i](); } })(); // for..of.. (async function(){ for(let fn of taskList) { await fn(); } })();koa2洋蔥模型實(shí)現(xiàn)原理
koa2,大家都不陌生了。koa2的洋蔥模型,是怎么實(shí)現(xiàn)的呢?先來看下面的代碼:
const Koa = require("koa"); const app = new Koa(); // logger app.use(async (ctx, next) => { console.log(1); await next(); console.log(2); const rt = ctx.response.get("X-Response-Time"); console.log(`${ctx.method} ${ctx.url} - ${rt}`); }); // x-response-time app.use(async (ctx, next) => { console.log(3); const start = Date.now(); await next(); console.log(4); const ms = Date.now() - start; ctx.set("X-Response-Time", `${ms}ms`); }); // response app.use(async ctx => { console.log(5); ctx.body = "Hello World"; }); app.listen(3000); // 訪問node時(shí),代碼輸出如下: // 1 // 3 // 5 // 4 // 2 // GET / - 6ms
其實(shí)實(shí)現(xiàn)起來很簡單,app.use就是將所有的回調(diào)函數(shù)都塞進(jìn)了一個(gè)任務(wù)隊(duì)列里面,調(diào)用await next()的時(shí)候,會(huì)直接執(zhí)行隊(duì)列里面下一個(gè)任務(wù),直到下一個(gè)任務(wù)執(zhí)行完成,才會(huì)接著執(zhí)行后續(xù)的代碼。我們來簡單實(shí)現(xiàn)一下最基本的邏輯:
class TaskList { constructor(){ this.list = []; } use(fn) { fn && this.list.push(fn); } start() { const self = this; let idx = -1; const exec = function() { idx++; const fn = self.list[idx]; if(!fn) { return Promise.resolve(); } return Promise.resolve(fn(exec)) } exec(); } } const test1 = function() { return new Promise( resolve => { setTimeout(function(){ console.log("fn1"); resolve(); }, 2000); }); }; const taskList = new TaskList(); taskList.use(async next => { console.log(1); await next(); console.log(2); }); taskList.use(async next => { console.log(3); await test1(); await next(); console.log(4); }); taskList.use(async next => { console.log(5); await next(); console.log(6); }); taskList.use(async next => { console.log(7); }); taskList.start(); // 輸出: 1、3、fn1、5、7、6、4、2寫在后面
可以看到,使用async和await進(jìn)行異步操作,可以使代碼看起來更為清晰,簡單。我們可以用同步代碼的方式來書寫異步代碼。本文還探究了前端開發(fā)中很常見的任務(wù)隊(duì)列的相關(guān)問題。通過本文和上一篇文章,我自己也對js中的異步操作有了更深入,更全面的認(rèn)識。符合預(yù)期。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/103557.html
摘要:當(dāng)這個(gè)迭代器的方法被首次后續(xù)調(diào)用時(shí),其內(nèi)的語句會(huì)執(zhí)行到第一個(gè)后續(xù)出現(xiàn)的位置為止,后緊跟迭代器要返回的值。在這個(gè)回調(diào)函數(shù)里,我們使用第一個(gè)請求返回的,再次發(fā)起一個(gè)請求。 寫在前面 本文首發(fā)于公眾號:符合預(yù)期的CoyPan 后續(xù)文章:【JS基礎(chǔ)】從JavaScript中的for...of說起(下) - async和await 先來看一段很常見的代碼: const arr = [1, 2, ...
摘要:對于,除非使用箭頭函數(shù),它的回調(diào)函數(shù)的將會(huì)變化。使用測試下面的代碼,結(jié)果如下打印打印要點(diǎn)使用的規(guī)則要求所有回調(diào)函數(shù)必須使用箭頭函數(shù)。 譯者按: JS 騷操作。 原文:For vs forEach() vs for/in vs for/of in JavaScript 譯者: Fundebug 本文采用意譯,版權(quán)歸原作者所有 我們有多種方法來遍歷 JavaScript 的數(shù)組或者...
摘要:從開始,就在引入新功能,來幫助更簡單的方法來處理異步編程,幫助我們遠(yuǎn)離回調(diào)地獄。而則是為了更簡潔的使用而提出的語法,相比這種的實(shí)現(xiàn)方式,更為專注,生來就是為了處理異步編程。 從Promise開始,JavaScript就在引入新功能,來幫助更簡單的方法來處理異步編程,幫助我們遠(yuǎn)離回調(diào)地獄。 Promise是下邊要講的Generator/yield與async/await的基礎(chǔ),希望你已...
摘要:不幸的是,迭代器不能用來表示這樣的數(shù)據(jù)源。即使是的迭代器也是不夠的,因?yàn)樗氖钱惒降?,但是迭代器需要同步確定狀態(tài)。異步迭代器一個(gè)異步迭代器就像一個(gè)迭代器,除了它的方法返回一個(gè)的。 ES2018 新特性 異步迭代器(本文) 正則表達(dá)式反向(lookbehind)斷言 正則表達(dá)式 Unicode 轉(zhuǎn)義 非轉(zhuǎn)義序列的模板字符串 正則表達(dá)式 s/dotAll 模式 正則表達(dá)式命名捕獲組 對...
摘要:等待的基本語法該關(guān)鍵字的的意思就是讓編譯器等待并返回結(jié)果。這里并不會(huì)占用資源,因?yàn)橐婵梢酝瑫r(shí)執(zhí)行其他任務(wù)其他腳本或處理事件。接下來,我們寫一個(gè)火箭發(fā)射場景的小例子不是真的發(fā)射火箭 本文由云+社區(qū)發(fā)表 本篇文章,小編將和大家一起學(xué)習(xí)異步編程的未來——async/await,它會(huì)打破你對上篇文章Promise的認(rèn)知,竟然異步代碼還能這么寫! 但是別太得意,你需要深入理解Promise后,...
閱讀 2183·2021-10-08 10:21
閱讀 2657·2021-09-29 09:34
閱讀 3570·2021-09-22 15:51
閱讀 5086·2021-09-22 15:46
閱讀 2370·2021-08-09 13:42
閱讀 3502·2019-08-30 15:52
閱讀 2795·2019-08-29 17:13
閱讀 1620·2019-08-29 11:30