国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

現(xiàn)代JS中的流程控制:詳解Callbacks 、Promises 、Async/Await

shadowbook / 1923人閱讀

摘要:控制臺將顯示回調(diào)地獄通常,回調(diào)只能由一個異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡化異步編碼旅程異步編程是一項在中無法避免的挑戰(zhàn)。

JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化?

請思考以下代碼:

result1 = doSomething1();
result2 = doSomething2(result1);

大多數(shù)語言都處理每一行同步。第一行運行并返回結(jié)果。第二行在第一行完成后運行無論需要多長時間。

單線程處理

JavaScript在單個處理線程上運行。在瀏覽器選項卡中執(zhí)行時,其他所有內(nèi)容都會停止,因為在并行線程上不會發(fā)生對頁面DOM的更改;將一個線程重定向到另一個URL而另一個線程嘗試追加子節(jié)點是危險的。

這對用戶來說是顯而易見。例如,JavaScript檢測到按鈕單擊,運行計算并更新DOM。完成后,瀏覽器可以自由處理隊列中的下一個項目。

(旁注:其他語言如PHP也使用單個線程,但可以由多線程服務(wù)器(如Apache)管理。同時對同一個PHP運行時頁面的兩個請求可以啟動兩個運行隔離的實例的線程。)

使用回調(diào)進行異步

單線程引發(fā)了一個問題。當(dāng)JavaScript調(diào)用“慢”進程(例如瀏覽器中的Ajax請求或服務(wù)器上的數(shù)據(jù)庫操作)時會發(fā)生什么?這個操作可能需要幾秒鐘 - 甚至幾分鐘。瀏覽器在等待響應(yīng)時會被鎖定。在服務(wù)器上,Node.js應(yīng)用程序?qū)o法進一步處理用戶請求。

解決方案是異步處理。而不是等待完成,一個進程被告知在結(jié)果準(zhǔn)備好時調(diào)用另一個函數(shù)。這稱為callback,它作為參數(shù)傳遞給任何異步函數(shù)。例如:

doSomethingAsync(callback1);
console.log("finished");

// call when doSomethingAsync completes
function callback1(error) {
  if (!error) console.log("doSomethingAsync complete");
}

doSomethingAsync()接受一個回調(diào)函數(shù)作為參數(shù)(只傳遞對該函數(shù)的引用,因此幾乎沒有開銷)。doSomethingAsync()需要多長時間并不重要;我們所知道的是callback1()將在未來的某個時刻執(zhí)行。控制臺將顯示:

finished
doSomethingAsync complete
回調(diào)地獄

通常,回調(diào)只能由一個異步函數(shù)調(diào)用。因此可以使用簡潔的匿名內(nèi)聯(lián)函數(shù):

doSomethingAsync(error => {
  if (!error) console.log("doSomethingAsync complete");
});

通過嵌套回調(diào)函數(shù),可以串行完成一系列兩個或多個異步調(diào)用。例如:

async1((err, res) => {
  if (!err) async2(res, (err, res) => {
    if (!err) async3(res, (err, res) => {
      console.log("async1, async2, async3 complete.");
    });
  });
});

不幸的是,這引入了回調(diào)地獄 - 一個臭名昭著的概念(http://callbackhell.com/) !代碼難以閱讀,并且在添加錯誤處理邏輯時會變得更糟。

回調(diào)地獄在客戶端編碼中相對較少。如果您正在進行Ajax調(diào)用,更新DOM并等待動畫完成,它可以深入兩到三個級別,但它通常仍然可以管理。

操作系統(tǒng)或服務(wù)器進程的情況不同。Node.js API調(diào)用可以接收文件上載,更新多個數(shù)據(jù)庫表,寫入日志,并在發(fā)送響應(yīng)之前進行進一步的API調(diào)用。

Promises

ES2015(ES6)推出了Promises。回調(diào)仍然可以使用,但Promises提供了更清晰的語法chains異步命令,因此它們可以串行運行(更多相關(guān)內(nèi)容)。

要啟用基于Promise的執(zhí)行,必須更改基于異步回調(diào)的函數(shù),以便它們立即返回Promise對象。該promises對象在將來的某個時刻運行兩個函數(shù)之一(作為參數(shù)傳遞):

resolve :處理成功完成時運行的回調(diào)函數(shù)

reject :發(fā)生故障時運行的可選回調(diào)函數(shù)。

在下面的示例中,數(shù)據(jù)庫API提供了一個接受回調(diào)函數(shù)的connect()方法。外部asyncDBconnect()函數(shù)立即返回一個新的Promise,并在建立連接或失敗后運行resolve()或reject():

const db = require("database");

// connect to database
function asyncDBconnect(param) {

  return new Promise((resolve, reject) => {

    db.connect(param, (err, connection) => {
      if (err) reject(err);
      else resolve(connection);
    });

  });

}

Node.js 8.0+提供了util.promisify()實用程序,將基于回調(diào)的函數(shù)轉(zhuǎn)換為基于Promise的替代方法。有幾個條件:

將回調(diào)作為最后一個參數(shù)傳遞給異步函數(shù)

回調(diào)函數(shù)必須指向一個錯誤,后跟一個值參數(shù)。

例子:

// Node.js: promisify fs.readFile
const
  util = require("util"),
  fs = require("fs"),
  readFileAsync = util.promisify(fs.readFile);

readFileAsync("file.txt");

各種客戶端庫也提供promisify選項,但您可以自己創(chuàng)建幾個:

// promisify a callback function passed as the last parameter
// the callback function must accept (err, data) parameters
function promisify(fn) {
  return function() {
      return new Promise(
        (resolve, reject) => fn(
          ...Array.from(arguments),
        (err, data) => err ? reject(err) : resolve(data)
      )
    );
  }
}

// example
function wait(time, callback) {
  setTimeout(() => { callback(null, "done"); }, time);
}

const asyncWait = promisify(wait);

ayscWait(1000);
異步鏈

任何返回Promise的東西都可以啟動.then()方法中定義的一系列異步函數(shù)調(diào)用。每個都傳遞了上一個解決方案的結(jié)果:

asyncDBconnect("http://localhost:1234")
  .then(asyncGetSession)      // passed result of asyncDBconnect
  .then(asyncGetUser)         // passed result of asyncGetSession
  .then(asyncLogAccess)       // passed result of asyncGetUser
  .then(result => {           // non-asynchronous function
    console.log("complete");  //   (passed result of asyncLogAccess)
    return result;            //   (result passed to next .then())
  })
  .catch(err => {             // called on any reject
    console.log("error", err);
  });

同步函數(shù)也可以在.then()塊中執(zhí)行。返回的值將傳遞給下一個.then()(如果有)。

.catch()方法定義了在觸發(fā)任何先前拒絕時調(diào)用的函數(shù)。此時,不會再運行.then()方法。您可以在整個鏈中使用多個.catch()方法來捕獲不同的錯誤。

ES2018引入了一個.finally()方法,無論結(jié)果如何都運行任何最終邏輯 - 例如,清理,關(guān)閉數(shù)據(jù)庫連接等。目前僅支持Chrome和Firefox,但技術(shù)委員會39已發(fā)布了 .finally() polyfill.

function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // tidy-up here!
  });
}
使用Promise.all()進行多個異步調(diào)用

Promise .then()方法一個接一個地運行異步函數(shù)。如果順序無關(guān)緊要 - 例如,初始化不相關(guān)的組件 - 同時啟動所有異步函數(shù)并在最后(最慢)函數(shù)運行解析時結(jié)束更快。

這可以通過Promise.all()來實現(xiàn)。它接受一組函數(shù)并返回另一個Promise。例如:

Promise.all([ async1, async2, async3 ])
  .then(values => {           // array of resolved values
    console.log(values);      // (in same order as function array)
    return values;
  })
  .catch(err => {             // called on any reject
    console.log("error", err);
  });

如果任何一個異步函數(shù)調(diào)用失敗,則Promise.all()立即終止。

使用Promise.race的多個異步調(diào)用()

Promise.race()與Promise.all()類似,只是它會在first Promise解析或拒絕后立即解析或拒絕。只有最快的基于Promise的異步函數(shù)才能完成:

Promise.race([ async1, async2, async3 ])
  .then(value => {            // single value
    console.log(value);
    return value;
  })
  .catch(err => {             // called on any reject
    console.log("error", err);
  });
但是有什么別的問題嗎?

Promises 減少了回調(diào)地獄但引入了別的問題。

教程經(jīng)常沒有提到_整個Promise鏈?zhǔn)钱惒降摹J褂靡幌盗衟romise的任何函數(shù)都應(yīng)返回自己的Promise或在最終的.then(),. catch()或.finally()方法中運行回調(diào)函數(shù)。

學(xué)習(xí)基礎(chǔ)知識至關(guān)重要。

更多的關(guān)于Promises的資源:

MDN Promise文檔

JavaScript Promises: 簡介

JavaScript Promises …相關(guān)細(xì)節(jié)

Promises異步編程

Async/Await

Promises 可能令人生畏,因此ES2017引入了async and await。 雖然它可能只是語法糖,它使Promise更完善,你可以完全避免.then()鏈。 考慮下面的基于Promise的示例:

function connect() {

  return new Promise((resolve, reject) => {

    asyncDBconnect("http://localhost:1234")
      .then(asyncGetSession)
      .then(asyncGetUser)
      .then(asyncLogAccess)
      .then(result => resolve(result))
      .catch(err => reject(err))

  });
}

// run connect (self-executing function)
(() => {
  connect();
    .then(result => console.log(result))
    .catch(err => console.log(err))
})();

用這個重寫一下async/await:

外部函數(shù)必須以async語句開頭

對異步的基于Promise的函數(shù)的調(diào)用必須在await之前,以確保在下一個命令執(zhí)行之前完成處理。

async function connect() {

  try {
    const
      connection = await asyncDBconnect("http://localhost:1234"),
      session = await asyncGetSession(connection),
      user = await asyncGetUser(session),
      log = await asyncLogAccess(user);

    return log;
  }
  catch (e) {
    console.log("error", err);
    return null;
  }

}

// run connect (self-executing async function)
(async () => { await connect(); })();

await有效地使每個調(diào)用看起來好像是同步的,而不是阻止JavaScript的單個處理線程。 此外,異步函數(shù)總是返回一個Promise,因此它們可以被其他異步函數(shù)調(diào)用。

async/await 代碼可能不會更短,但有相當(dāng)大的好處:

1、語法更清晰。括號更少,錯誤更少。

2、調(diào)試更容易。可以在任何await語句上設(shè)置斷點。
3、錯誤處理更好。try / catch塊可以與同步代碼一樣使用。

4、支持很好。它在所有瀏覽器(IE和Opera Mini除外)和Node 7.6+中都得到了支持。

但是并非所有都是完美的......

切勿濫用async/await

async / await仍然依賴于Promises,它最終依賴于回調(diào)。你需要了解Promises是如何工作的,并且沒有Promise.all()和Promise.race()的直接等價物。并且不要忘記Promise.all(),它比使用一系列不相關(guān)的await命令更有效。

同步循環(huán)中的異步等待

在某些時候,您將嘗試調(diào)用異步函數(shù)中的同步循環(huán)。例如:

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}

它不會起作用。這也不會:

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}

循環(huán)本身保持同步,并且總是在它們的內(nèi)部異步操作之前完成。

ES2018引入了異步迭代器,它與常規(guī)迭代器一樣,但next()方法返回Promise。因此,await關(guān)鍵字可以與for循環(huán)一起用于串行運行異步操作。例如:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}

但是,在實現(xiàn)異步迭代器之前,最好將數(shù)組項映射到異步函數(shù)并使用Promise.all()運行它們。例如:

const
  todo = ["a", "b", "c"],
  alltodo = todo.map(async (v, i) => {
    console.log("iteration", i);
    await processSomething(v);
});

await Promise.all(alltodo);

這具有并行運行任務(wù)的好處,但是不可能將一次迭代的結(jié)果傳遞給另一次迭代,并且映射大型數(shù)組可能在性能消耗上是很昂貴。

try/catch 有哪些問題了?

如果省略任何await失敗的try / catch,async函數(shù)將以靜默方式退出。如果您有一組很長的異步await命令,則可能需要多個try / catch塊。

一種替代方案是高階函數(shù),它捕獲錯誤,因此try / catch塊變得不必要(thanks to @wesbos for the suggestion):

async function connect() {

  const
    connection = await asyncDBconnect("http://localhost:1234"),
    session = await asyncGetSession(connection),
    user = await asyncGetUser(session),
    log = await asyncLogAccess(user);

  return true;
}

// higher-order function to catch errors
function catchErrors(fn) {
  return function (...args) {
    return fn(...args).catch(err => {
      console.log("ERROR", err);
    });
  }
}

(async () => {
  await catchErrors(connect)();
})();

但是,在應(yīng)用程序必須以與其他錯誤不同的方式對某些錯誤做出反應(yīng)的情況下,此選項可能不實用。

盡管有一些陷阱,async / await是JavaScript的一個優(yōu)雅補充。更多資源:

MDN async and await

Async functions – 使 promises 更友好

TC39 Async Functions 規(guī)范

使用異步函數(shù)簡化異步編碼

JavaScript 旅程

異步編程是一項在JavaScript中無法避免的挑戰(zhàn)。回調(diào)在大多數(shù)應(yīng)用程序中都是必不可少的,但它很容易陷入深層嵌套的函數(shù)中。

Promises 抽象回調(diào),但有許多語法陷阱。 轉(zhuǎn)換現(xiàn)有函數(shù)可能是一件苦差事,而.then()鏈仍然看起來很混亂。

幸運的是,async / await提供了清晰度。代碼看起來是同步的,但它不能獨占單個處理線程。它將改變你編寫JavaScript的方式!

(譯者注:Craig Buckler講解JavaScript的文章都還不錯,基本是用一些比較通俗的語言和代碼事例講解了JavaScript的一些特性和一些語法可能出現(xiàn)的問題。感興趣的朋友可以看一下(https://www.sitepoint.com/aut...))

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/113778.html

相關(guān)文章

  • 現(xiàn)代JS中的流程控制詳解CallbacksPromisesAsync/Await

    摘要:控制臺將顯示回調(diào)地獄通常,回調(diào)只能由一個異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡化異步編碼旅程異步編程是一項在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...

    oujie 評論0 收藏0
  • 現(xiàn)代JS中的流程控制詳解CallbacksPromisesAsync/Await

    摘要:控制臺將顯示回調(diào)地獄通常,回調(diào)只能由一個異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡化異步編碼旅程異步編程是一項在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...

    anquan 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • 翻譯:Taming the asynchronous beast with ES7

    摘要:讓我們使用它從數(shù)組中返回一個值數(shù)組在中,我們可以這樣做,這是一種更簡單的方法最重要的部分是創(chuàng)建數(shù)組,該數(shù)組立即調(diào)用所有的我們在主函數(shù)中等待這些。所以在我們真正等待完成之前,主函數(shù)就退出了。 原文:https://pouchdb.com/2015/03/0... PouchDB最棘手的方面之一是它的API是異步的。在Stack Overflow、Github和IRC上,我看到了不少困惑的...

    Eastboat 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<