ホームページ ウェブフロントエンド jsチュートリアル JavaScript非同期関数の開発経緯を語る_JavaScriptスキル

JavaScript非同期関数の開発経緯を語る_JavaScriptスキル

May 16, 2016 pm 03:37 PM

外国語の「非同期 JavaScript の進化」では、JavaScript 非同期関数の開発の歴史を整理しました。まず、非同期関数はコールバック関数を通じて実現され、その後、Promise/A およびジェネレーター関数を経て実現されることになります。非同期関数であること。この記事を翻訳してくれた Jingzhuang に感謝します。内容は次のとおりです:

ここで、長年にわたる JavaScript 非同期関数の開発を振り返ってみましょう。

コールバック関数 Callbacks

コールバック関数からすべてを開始する必要があるようです。

非同期 JavaScript

ご存知のとおり、JavaScript では、非同期プログラミングは JavaScript 言語の第一級市民関数を通じてのみ実行できます。この方法は、ある関数を別の関数のパラメーターとして渡すことができることを意味します。関数内で呼び出されます (つまり、コールバック関数)。これが、コールバック関数が生まれた理由です。関数をパラメータとして別の関数 (この時点では高階関数と呼ばれます) に渡すと、関数内でこの関数を呼び出して、対応するタスクを完了できます。コールバック関数には戻り値がなく (リターンを使用しないでください)、関数内で特定のアクションを実行するためにのみ使用されます。 例を見てみましょう:

Something.save(function(err) {  
 if (err) { 
  //error handling 
  return; // 没有返回值 
 } 
 console.log('success'); 
}); 
ログイン後にコピー

上記の例では、Node.js のすべてのコア モジュールと NPM リポジトリのほとんどのモジュールの特徴の 1 つでもあるエラー優先コールバック関数 (error-first callbacks) を示しました。書き込み時にはこの機能に従います。

コールバック関数の多用に関する課題:

コードを合理的に整理できないと、コールバック地獄 (コールバック地獄) が発生しやすくなり、コードが他の人に理解されにくくなります。
エラー処理コードは見落とされがちです。

return ステートメントを使用して値を返すことはできません。また、throw キーワードは使用できません。

これらの理由から、JavaScript の世界では、非同期 JavaScript 開発を容易にする実現可能なソリューションを常に探しています。

考えられる解決策の 1 つは、async モジュールです。コールバック関数を長い間扱ってきた人であれば、JavaScript で何かを並列または直列に実行したり、非同期関数を使用して配列内の何かをマップしたりする場合、どれほど複雑であるかを深く理解しているかもしれません。は非同期関数を使用する要素です。したがって、これらの問題を解決する非同期モジュールを作成してくれた Caolan McMahon に感謝します。

async モジュールを使用すると、次の方法でコードを簡単に作成できます。

async.map([1, 2, 3], AsyncSquaringLibrary.square,  
 function(err, result){ 
 // result will be [1, 4, 9] 
}); 
ログイン後にコピー

async モジュールはある程度の利便性をもたらしますが、まだ単純ではなく、コードが読みにくいため、Promise が登場しました。

約束

現在の JavaScript 非同期標準は 2012 年に遡り、ES6 まで利用可能になりませんでした。ただし、Promise という用語は JavaScript コミュニティによって発明されたものではありません。この用語は、1976 年にダニエル P. フリードマンによって発表された記事に由来しています。

Promise は、非同期操作の最終結果を表します。

ここで、Promise を使用して、上記のコードで完了したタスクを完了します。Promise スタイルのコードは次のとおりです。

Something.save()  
 .then(function() { 
  console.log('success'); 
 }) 
 .catch(function() { 
  //error handling 
 }) 
ログイン後にコピー

Promise でもコールバック関数が使用されていることがわかります。コールバック関数は then メソッドと catch メソッドの両方に渡され、Promise が満たされた場合と拒否された場合にそれぞれ実行されます。 Promise 関数のもう 1 つの利点は、関数を連鎖させて一連のタスクを完了できることです。たとえば、次のようなコードを書くことができます:

saveSomething()  
 .then(updateOtherthing) 
 .then(deleteStuff)  
 .then(logResults); 
ログイン後にコピー

既製の Promise がない場合は、いくつかの Promise ライブラリを使用する必要がある場合があります。一般的な選択肢は、bluebird を使用することです。 これらのライブラリは、ネイティブ ソリューションよりも多くの機能を提供する可能性があり、Promise/A 標準で指定された機能に限定されません。

でも、なぜ砂糖を使った方法を使わないのでしょうか?最初に「Promise: 拡張機能の問題」という記事を読むことをお勧めします。 Promise の詳細については、Promise/A 標準を参照してください。

ほとんどのライブラリがコールバック インターフェイスのみを公開する場合、どのように Promise を使用すればよいのでしょうか?

これは非常に簡単です。現時点で行う必要があるのは、Promise を使用してコールバックを含む関数呼び出し本体をラップすることだけです。例:

コールバック スタイルのコードは次のようになります:

function saveToTheDb(value) { 
  db.values.insert(value, function (err, user) { 
    if (err) throw err; 
 
    // todo: insert user to db 
  }); 
} 
ログイン後にコピー

ここで、Promise スタイルの呼び出しをサポートするコードに変更します。

function saveToTheDb(value) {  
  return new Promise(function(resolve, reject) { 
    db.values.insert(value, function(err, user) { // remember error first ;) 
      if (err) { 
        return reject(err); // don't forget to return here 
      } 
      resolve(user); 
    }) 
  } 
} 
ログイン後にコピー

かなりの数のライブラリまたはフレームワークがすでに両方のメソッドをサポートしています。つまり、コールバック スタイルと Promise スタイルの API インターフェイスの両方を提供しています。したがって、ライブラリを外部にも提供したい場合、ベスト プラクティスは両方のインターフェイスを同時に提供することです。この目的を達成するには、次のメソッドを簡単に使用できます:

function foo(cb) {  
 if (cb) { 
  return cb(); 
 } 
 return new Promise(function (resolve, reject) { 
 
 }); 
} 
ログイン後にコピー

或者更简单些,你可以从只提供Promise风格的接口开始后,并使用诸如 callbackify这样的工具来达到向后兼容的目的。其实Callbackify所做的工作和上面的代码片段类似,但在实现上使用了一个更通用的方法, 我建议你可以去阅读Callbackify的源代码。

生成器Generators/ yield

JavaScript 生成器是个相对较新的概念, 它是ES6(也被称为ES2015)的新特性。想象下面这样的一个场景:

当你在执行一个函数的时候,你可以在某个点暂停函数的执行,并且做一些其他工作,然后再返回这个函数继续执行, 甚至是携带一些新的值,然后继续执行。
上面描述的场景正是JavaScript生成器函数所致力于解决的问题。当我们调用一个生成器函数的时候,它并不会立即执行, 而是需要我们手动的去执行迭代操作(next方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器会遍历每个中断点。

function* foo () {  
 var index = 0; 
 while (index < 2) { 
  yield index++; //暂停函数执行,并执行yield后的操作 
 } 
} 
var bar = foo(); // 返回的其实是一个迭代器 
console.log(bar.next());  // { value: 0, done: false }  
console.log(bar.next());  // { value: 1, done: false }  
console.log(bar.next());  // { value: undefined, done: true } 
ログイン後にコピー

更进一步的,如果你想更轻松的使用生成器函数来编写异步JavaScript代码,我们可以使用 co 这个库,co是著名的tj大神写的。

Co是一个为Node.js和浏览器打造的基于生成器的流程控制工具,借助于Promise,你可以使用更加优雅的方式编写非阻塞代码。
使用co,前面的示例代码,我们可以使用下面的代码来改写:

co(function* (){  
 yield Something.save(); 
}).then(function() { 
 // success 
}) 
.catch(function(err) { 
 //error handling 
}); 
ログイン後にコピー

你可能会问:如何实现并行操作呢?答案可能比你想象的简单,如下(其实它就是Promise.all而已):

yield [Something.save(), Otherthing.save()];  
Async/ await
ログイン後にコピー

在ES7(还未正式标准化)中引入了Async函数的概念,目前如果你想要使用的话,只能借助于babel 这样的语法转换器将其转为ES5代码。(提醒一点:我们现在讨论的是async关键字,而不是NPM中的async包)。

简而言之,使用async关键字,你可以轻松地达成之前使用生成器和co函数所做到的工作。当然,除了hack之外。

也许你会问,是否在ES7中有了async关键字,yield就变得不是那么重要了?

实际上,使用yield实现异步也不过是一种hack罢了,yield意味着懒次序(lazy sequences)和迭代器。 而await能够完美的分离这两点,首先让yield用于其最初的目的,其次使用await来执行异步操作。

在这背后,async函数实际使用的是Promise,也就是为什么async函数会返回一个Promise的原因。

因此,我们使用async函数来完成类似于前面代码所完成的工作,可以使用下面这样的方式来重新编写代码:

async function save(Something) {  
 try { 
  await Something.save(); // 等待await后面的代码执行完,类似于yield 
 } catch (ex) { 
  //error handling 
 } 
 console.log('success'); 
} 
ログイン後にコピー

正如你看到的那样,使用async函数,你需要在函数声明的最前面加上async关键字。这之后,你可以在函数内部使用await关键字了,作用和之前的yield作用是类似的。

使用async函数完成并行任务与yiled的方式非常的相似,唯一不同的是,此时Promise.all不再是隐式的,你需要显示的调用它:

async function save(Something) {  
  await Promise.all[Something.save(), Otherthing.save()] 
} 
ログイン後にコピー

Koa也支持async函数,如果你也在使用koa,那么你现在就可以借助babel使用这一特性了。

import koa from koa;  
let app = koa(); 
app.experimental = true; 
app.use(async function (){  
  this.body = await Promise.resolve('Hello Reader!') 
}) 
app.listen(3000);  
ログイン後にコピー

以上内容给大家分享了JavaScript异步函数发展历程,希望对大家有所帮助。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットな記事タグ

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JavaScriptの文字列文字を交換します JavaScriptの文字列文字を交換します Mar 11, 2025 am 12:07 AM

JavaScriptの文字列文字を交換します

カスタムGoogle検索APIセットアップチュートリアル カスタムGoogle検索APIセットアップチュートリアル Mar 04, 2025 am 01:06 AM

カスタムGoogle検索APIセットアップチュートリアル

例JSONファイルの例 例JSONファイルの例 Mar 03, 2025 am 12:35 AM

例JSONファイルの例

独自のAjax Webアプリケーションを構築します 独自のAjax Webアプリケーションを構築します Mar 09, 2025 am 12:11 AM

独自のAjax Webアプリケーションを構築します

8見事なjQueryページレイアウトプラグイン 8見事なjQueryページレイアウトプラグイン Mar 06, 2025 am 12:48 AM

8見事なjQueryページレイアウトプラグイン

' this' JavaScriptで? ' this' JavaScriptで? Mar 04, 2025 am 01:15 AM

' this' JavaScriptで?

ソースビューアーでjQueryの知識を向上させます ソースビューアーでjQueryの知識を向上させます Mar 05, 2025 am 12:54 AM

ソースビューアーでjQueryの知識を向上させます

モバイル開発用のモバイルチートシート10個 モバイル開発用のモバイルチートシート10個 Mar 05, 2025 am 12:43 AM

モバイル開発用のモバイルチートシート10個

See all articles