非同期とは、CPU の占有率を高め、常に CPU をビジー状態に保つことです。
一部の操作 (最も一般的なものは I/O) は CPU の関与を必要とせず、非常に時間がかかります。非同期が使用されない場合、ブロック状態が形成され、CPU がアイドル状態になり、ページがフリーズします。
非同期環境で I/O 操作が発生すると、CPU は I/O 作業を脇に置き (この時点では、I/O は他のコントローラーによって引き継がれ、データはまだ送信されています)、次のタスクを処理します。 、I/O 操作が完了するのを待機し、CPU に通知して (コールバックは通知メソッドです)、動作に戻るようにします。
「JavaScript の非同期とコールバック」の主な内容は、非同期作業の具体的な終了時刻が不確実であることです。非同期作業が完了した後に後続の処理を正確に実行するには、After が行われるようにコールバックを非同期関数に渡す必要があります。作業が完了したら、次のタスクに進みます。
コールバックは非同期で実装するのが非常に簡単ですが、複数のネストが原因でコールバック地獄が形成される可能性があります。コールバック地獄を回避するには、ネストしてネストされたプログラミングを線形プログラミングに変更する必要があります。
Promise
JavaScript
でのコールバック地獄を処理するための最良のソリューションです。
Promise
は「約束」と訳せます。非同期作業をカプセル化してPromise
と呼ぶことができます。つまり、非同期作業が終了した後に明確な信号を与えることを約束します。
Promise
構文:
let Promise = new Promise(function(resolve,reject){ // 非同期作業})
上記の構文を通じて、非同期作業をPromise
にカプセル化できます。 Promise
作成時に渡される関数は、非同期作業を処理するメソッドであり、 executor
キュータとも呼ばれます。
resolve
とreject
JavaScript
自体によって提供されるコールバック関数です。これらは、 executor
タスクを完了するときに呼び出すことができます。resolve
resolve(result)
- 正常に完了した場合はresult
が返され、error
がreject(error)
した場合はerror
が生成されます。Executorexecutor
Promise
が作成された直後に自動的に実行され、その実行ステータスはPromise
の内部プロパティの状態を変更します。
state
- 最初はpending
、その後、 resolve
が呼び出された後にfulfilled
に変換されるか、 rejected
されました。fs.readFile
reject
error
reject
result
undefined
value
resolve(value)
ファイル読み取りexecutor
はファイル内で実行されるため、非同期作業がカプセル化されます。
次のコードはfs.readFile
関数をカプセル化し、 resolve(data)
を使用して成功した結果を処理し、 reject(err)
使用して失敗した結果を処理します。
コードは次のとおりです。
let Promise = new Promise((resolve,拒否) => { fs.readFile('1.txt', (err, data) => { console.log('1.txtの読み取り') if (エラー) 拒否 (エラー) 解決(データ) })})
このコードを実行すると「Read 1.txt」という文字が出力され、 Promise
の作成直後にファイルの読み込み操作が行われたことがわかります。
Promise
通常、非同期コードを内部的にカプセル化しますが、非同期コードをカプセル化するだけではありません。
上記のPromise
ケースでは、ファイルの作成が完了するとすぐにファイルの読み取り操作がカプセル化されます。 Promise
の実行結果を取得したい場合は、 then
、 catch
、 finally
3つのメソッドを使用する必要があります。
Promise
のthen
メソッドは、 Promise
実行が完了した後の作業を処理するために使用できます。構文は次のとおりです。promise.then
function(result),function(error))。
result
、 resolve
によって受け取られた値です。2error
は、 reject
によって受け取られたパラメータです
。
promise = new Promise((解決、拒否) => { fs.readFile('1.txt', (err, data) => { console.log('1.txtの読み取り') if (エラー) 拒否 (エラー) 解決(データ) })})約束.then( (データ) => { console.log('正常に実行されました。結果は次のとおりです' + data.toString()) }、 (エラー) => { console.log('実行に失敗しました。エラーは' + err.message) })
ファイルの読み取りが正常に実行されると、最初の関数が呼び出されます:
PS E:CodeNodedemos 3-callback> node .index.js 1.txtを読む 正常に実行された場合、結果は 1
delete 1.txt
になります。実行が失敗した場合は、2 番目の関数が呼び出されます:
PS E:CodeNodedemos 3-callback> node .index.js 1.txtを読む 実行はエラー ENOENT: no such file or directory, open 'E:CodeNodedemos 3-callback1.txt' で失敗しました。
実行が成功した結果のみに注目する場合、渡すことができるのは 1 つだけです。コールバック関数:
promise .then((data)=>{ console.log('正常に実行されました。結果は次のとおりです' + data.toString())})
この時点で、ファイルの非同期読み取り操作が実装されました。
失敗の結果のみに注目する場合は、 promise.then(null,(err)=>{...})
のように、最初のthen
コールバックにnull
渡すことができます。
または、より洗練された方法を使用します: promise.catch((err)=>{...})
let Promise = new Promise((resolve, respect) => { fs.readFile('1.txt', (err, data) => { console.log('1.txtの読み取り') if (エラー) 拒否 (エラー) 解決(データ) })})promise.catch((err)=>{ console.log(err.message)})
.catch((err)=>{...})
とthen(null,(err)=>{...})
はまったく同じ効果があります。
.finally
は、 promise
の結果に関係なく実行される関数で、 try...catch...
構文のfinally
と同じ目的を持ち、結果に関係のない操作を処理できます。
例:
new Promise((resolve,reject)=>{ //何か...}).finally(()=>{console.log('結果に関係なく実行')}).then(result=>{...}, err=>{...} )finally コールバックにはパラメータがなく
fs.readFile()
メソッドは 10 個のファイルを順番に読み込んで内容を出力します。 10 個のファイルを順番に。
fs.readFile()
自体は非同期であるため、コールバックのネストを使用する必要があります。コードは次のとおりです。
fs.readFile('1.txt', (err, data) => { console.log(data.toString()) //1 fs.readFile('2.txt', (err, データ) => { console.log(data.toString()) fs.readFile('3.txt', (err, データ) => { console.log(data.toString()) fs.readFile('4.txt', (err, データ) => { console.log(data.toString()) fs.readFile('5.txt', (err, データ) => { console.log(data.toString()) fs.readFile('6.txt', (err, データ) => { console.log(data.toString()) fs.readFile('7.txt', (err, データ) => { console.log(data.toString()) fs.readFile('8.txt', (err, data) => { console.log(data.toString()) fs.readFile('9.txt', (err, data) => { console.log(data.toString()) fs.readFile('10.txt', (err, データ) => { console.log(data.toString()) // ==> 地獄の門}) }) }) }) }) }) }) }) })})
上記のコードでタスクを完了できますが、呼び出しのネストが増えると、コード レベルが深くなり、特に、 の代わりに多くのループや条件文を含む実際のコードを使用する場合、メンテナンスの難易度が高くなります。例では単純なconsole.log(...)
。
コールバックを使用せず、次のコードに従ってfs.readFile()
順番に直接呼び出した場合、何が起こるでしょうか。
//注意: これは間違った書き方です fs.readFile('1.txt', (err, data) => { console.log(data.toString())})fs.readFile('2.txt', (err, data) => { console.log(data.toString())})fs.readFile('3.txt', (err, data) => { console.log(data.toString())})fs.readFile('4.txt', (err, data) => { console.log(data.toString())})fs.readFile('5.txt', (err, data) => { console.log(data.toString())})fs.readFile('6.txt', (err, data) => { console.log(data.toString())})fs.readFile('7.txt', (err, data) => { console.log(data.toString())})fs.readFile('8.txt', (err, data) => { console.log(data.toString())})fs.readFile('9.txt', (err, data) => { console.log(data.toString())})fs.readFile('10.txt', (err, data) => { console.log(data.toString())})
以下は私のテストの結果です (各実行の結果は異なります):
PS E:CodeNodedemos 3-callback> node .index. js12346957108 が
この非順次結果を生成する理由は非同期であり、マルチスレッド並列処理ではありません。非同期は単一スレッドで実現できます。
ここでこのエラーのケースが使用されているのは、非同期性の概念を強調するためです。なぜこの結果が発生するのか理解できない場合は、レッスンに戻って補う必要があります。
Promise
を使用して非同期シーケンシャル ファイル読み取りを解決するというアイデア:
promise1
を読み取り、 resolve
を使用しpromise1.then
ファイル読み取り結果を受け取り、出力します。promise2
オブジェクトを作成します。そしてpromise1.then
promise2.then
の呼び出しにpromise2.then
で新しいpromise3
の呼び出しに戻り、promise3.then
結果を受け取り、出力し。コードは次のとおりです。
let Promise1 = new Promise( (解決、拒否) => { fs.readFile('1.txt', (err, data) => { if (エラー) 拒否 (エラー) 解決(データ) })})プロミス 2 = プロミス 1.then( データ => { console.log(data.toString()) return new Promise((解決、拒否) => { fs.readFile('2.txt', (err, データ) => { if (エラー) 拒否 (エラー) 解決(データ) }) }) })プロミス3 = プロミス2.then( データ => { console.log(data.toString()) return new Promise((解決、拒否) => { fs.readFile('3.txt', (err, データ) => { if (エラー) 拒否 (エラー) 解決(データ) }) }) })プロミス4 = プロミス3.then( データ => { console.log(data.toString()) //.... })... ...
このようにして、元のネストされたコールバック ヘルを線形モードに書き込みます。
しかし、コードにはまだ問題があり、管理の点ではコードはより美しくなりましたが、コードの長さは大幅に増加します。
上記のコードは長すぎます。次の 2 つの手順でコードの量を減らすことができます。
promise
の変数作成を省略し、 .then
をリンクします。次のようなコード:
function myReadFile (path) { return new Promise((解決、拒否) => { fs.readFile(path, (err, data) => { if (エラー) 拒否 (エラー) console.log(data.toString()) 解決する() }) })}myReadFile('1.txt') .then(data => { return myReadFile('2.txt') }) .then(data => { return myReadFile('3.txt') }) .then(data => { return myReadFile('4.txt') }) .then(data => { return myReadFile('5.txt') }) .then(data => { return myReadFile('6.txt') }) .then(data => { return myReadFile('7.txt') }) .then(data => { return myReadFile('8.txt') }) .then(data => { return myReadFile('9.txt') }) .then(data => { return myReadFile('10.txt') })
myReadFile
メソッドは新しいPromise
を返すため、 .then
メソッドを直接実行できます。このプログラミング方法はチェーン プログラミングと呼ばれます。
コードの実行結果は次のとおりです。
PS E:CodeNodedemos 3-callback> node .index.js12345678910
これで、非同期および順次のファイル読み取り操作が完了します。
注: 新しい
Promise
オブジェクトは各ステップの.then
メソッドで返される必要があります。返されない場合は、以前の古いPromise
が受信されます。これは、各
then
メソッドがそのPromise
下方に渡し続けるためです。