
非同期とは、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
rejecterror 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.thenpromise2.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下方に渡し続けるためです。