<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                在JavaScript中做異步開發時,我們現在會毫不猶豫的使用`Async/Await`。不管是并發還是串行,`Async/Await`都能處理的很好,而且還保證了代碼的可讀性。但是,`Async/Await`是在ES6之后突然出現的, 這激發了我的好奇心,使得我想更進一步的了解它。本文假設讀者是對Promise和Async有一定了解的,因此對基礎部分不做過多的敘述。然后,我將通過以下三個部分來剖析`Async\Await`: * Generator函數 * 自動執行器 * 內置執行 `Async`函數的實現原理就是將Generator函數和自動執行器包裝在一個函數里。 ### Generator函數 ### 基本用法 Generator函數是ES6提供的一種異步編程解決方案,它的調用方式與普通函數一樣,也是在函數后面加上一對圓括號。不同的是,調用Generator函數后,該函數并不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象(遍歷器對象)。接著,必須調用遍歷器對象的next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一條yield語句(或return語句)為止。 ~~~js function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); hw.next(); // { value: 'hello', done: false } hw.next(); // { value: 'world', done: false } hw.next(); // { value: 'ending', done: true } hw.next(); // { value: undefined, done: true } ~~~ ### 異步操作 Ajax是典型的異步操作,我們試著通過Generator函數部署Ajax,來達到同步表達的效果。 ~~~js function* main() { var result = yield request("url"); var res = JSON.parse(result); console.log(res.value) } function request(url) { makeAjaxCall(url, function(response) { it.next(response); }); } var it = main(); it.next(); ~~~ 這樣似乎能滿足我們的要求,但如果我們增加了請求的數量,那結果就不是我們期望的那樣美好了:我們需要不斷的調用next方法來逐個執行異步請求。如果Generator能夠自動執行就好了。 ~~~js function main() { var result1 = yield request('url1'); var res1 = JSON.parse(result1); console.log(res1.value); var result2 = yield request('url2'); var res2 = JSON.parse(result2); console.log(res2.value) } function request(url) { makeAjaxCall(url, function(response) { it.next(response); }); } var it = main(); it.next(); it.next(); ~~~ ### 自動執行器 如果要自動執行,我們首先就會想到用循環來處理。 ~~~js function* gen() [ ...... } var g = gen(); var res = g.next(); while(!res.done) { console.log(res.value); res = g.next(); } ~~~ 上面的代碼中,`gen`自動執行完所有步驟,但對于異步操作而言,必須保證前一步執行完才能執行后一步。根據經驗,JavaScript中的回調函數和Promise剛好滿足這個要求。 ### 基于Thunk(回調函數)的自動執行器 JavaScript中`Thunk`函數是用來將多參函數替換成只接受回調函數作為參數的單參函數。通過`Thunk`函數,我們可以將`next`方法作為回調函數傳給 需要異步操作的方法,當異步操作完成時,調用`next`方法從而執行下一個操作,從而達到異步操作同步執行的效果。 ~~~js var fs = require('fs'); var thunk = function(fn) { return function() { var args = Array.prototype.slice.call(arguments); return function(callback) { args.push(callback); return fn.apply(this, args); } } }; var readFileThunk = thunk(fs.readFile); function run(fn) { var gen = fn(); function next(err, data) { var result = gen.next(data); if(result.done) return; result.value(next); } next(); } function* g() { var f1 = yield readFile('fileA'); var f2 = yield readFile('fileB'); // ... var fn = yield readFile('fileN'); } run(g); ~~~ ### 基于Promise對象的自動執行器 除了thunk之外,Promise也可以實現自動執行器。這其中的原理也是類似的,也是在確保異步操作完成的情況下調用`next`方法,從而執行下一次的異步操作。 ~~~js var fs = require('fs'); var readFile = function(fileName) { return new Promise(function(resolve, reject) { fs.readFile(fileName, function(resolve, reject) { if(error) return reject(error); resolve(data); }); }); }; var gen = function* () { var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); } function run(gen) { var g = gen(); function next(data) { var result = g.next(data); if(result.done) return result.value; result.value.then(function(data) { next(data); }); } next(); } run(gen); ~~~ ### 內置執行 前面我們通過Generator函數寫了讀取文件的代碼,接下來我們將它們改成async函數的形式。 ~~~js ...... var asyncReadFile = async function() { var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()) } ~~~ 通過比較就會發現,async函數就是將Generator函數的星號(\*)替換成async,將yield替換成await,然后內置了一個功能類似`run`的執行器。我們用代碼模擬下一下這個執行器。 ~~~js async function fn(args) { //...... } // 等同于 function fn(args) { return spawn(function* () { //...... }); } function spawn(genF) { return new Promise(function(resolve, reject) { var gen = genF(); function step(nextF) { try { var next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resovle(next.value).then(function(v) { step(function(){ return gen.next(v); }); }, function(e){ step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); } ~~~ ### 總結 本文從Generator函數、自動執行器再到內置執行,分拆解析了`Async`的原理,希望能對大家有所幫助。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看