JavaScript非同期promiseからawaitへ

5520 ワード

一、コールバックからPromiseまで、なぜそれを使うのか
非同期タスクを実行するときに、非同期タスクの終了時に呼び出されるコールバック関数が使用されます.将来実行される共通点があります.例えばnode.jsでの非同期読み込みファイル:
fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
});

ajaxリクエスト関数を書いたように、
//    ,     
const request = function(callback){
  let xhr = new XMLHttpRequest();
   // .....
  xhr.onreadystatechange = function(){
    callback(xhr)              
  }
}
//  
request(function(xhr){
  if (xhr.readyState === 4 && xhr.status === 200) {
      // ....
  } else {
      //....
  }  
})

タイマータスクもあります
const doSomethingLater = function(delay, callback){
    setTimeout(callback, delay*1000)
}
//   
doSomethingLater(1, function(){
  // .....
})

このようにコールバックを使用する方法は問題ないようですが、コールバック関数に非同期タスクがある場合、多層コールバック、すなわちコールバック地獄の問題が発生する可能性があります.多層コールバックにより、コードの可読性とメンテナンス性が低下します.
Promiseは私たちのために何をしましたか?
簡単に言えば、Promiseは非同期タスクをオブジェクトにパッケージし、非同期タスクが完了した後に実行する関数をthenメソッドに渡し、resolveで関数を呼び出します.上記のタイマタスクを次のように書き換えることができます.
const doSomethingLater = function(delay){
    return new Promise((resolve)=>{
      setTimeout(()=>{ resolve() }, delay*1000)
    })
}
doSomethingLater(1)
    .then(()=>{
      console.log('  1')
    })

タイミング・タスクでまたタイミング・タスクを実行する場合は、コールバックをネストするのではなく、このように書くことができます.
doSomethingLater(1)
    .then(() => {
        console.log('  1')
        return doSomethingLater(1)
    })
    .then(() => {
        console.log('  2')
    })

Promiseの役割:
  • 非同期タスクが完了した後の処理関数を場所を変えて置く:thenメソッドに伝え、チェーン呼び出しをサポートし、階層的なコールバックを回避する.
  • はエラーをキャプチャします.コードエラーでも手動reject()でも、これらのエラーを1つの関数で処理できます.

  • 二、あなたが知らないかもしれないPromiseの詳細
    Promiseを使うと非同期コードになります
    コードに非同期操作がなかったとしても
    Promise.resolve()
        .then(() => {
            console.log(1)
        })
    console.log(2)
    // 2
    // 1

    この点は,イベントループに関する知識を調べることができる.
    catchのもう一つの書き方
    Promise.reject('error')
        .then(() => {
        })
        .catch((err) => {
            console.log(err)
        })
    //    
    Promise.reject('error')
        .then(() => {
        })
        .then(null, (err) => {
            console.log(err)
        })
    //   
    Promise.reject('error')
        .then(() => {
        }, (err) => {
            console.log(err)
        })

    実はcatchは意味化された文法糖にすぎず、thenでエラーを直接処理することもできます.
    thenメソッドまたはcatchメソッドは常にpromiseオブジェクトを返します
    thenメソッドの最初のパラメータと2番目のパラメータ(またはcatchのパラメータ)は、呼び出し条件が異なるだけです.
    Promise.resolve()
        .then(() => {
            return 1
        })
        .then((res) => {
            console.log(res) // 1
        })
        
    Promise.resolve()
        .then(() => {
           //      
        })
        .then((res) => {
            console.log(res) // undefined,        undefined 
        })
       

    promiseオブジェクトを返す場合:
    Promise.resolve()
        .then(() => {
            return new Promise((resolve) => {
                resolve(2)
            })
        })
        .then((res) => {
            console.log(res) // 2,        promise      
        })

    パッケージを使用すると、promiseオブジェクトの最後の状態を常に成功させることができます.たとえば、次のようにします.
    const task = () => {
        return new Promise((resolve, reject) => {
            // ....
        })
    }
    task()
        .then((res) => {
            console.log(res)
        })
        .catch((err) => {
            console.log(err)
        })

    task関数を呼び出すには、外にcatchキャプチャエラーを追加する必要がありますが、パッケージすることができます.
    const task = () => {
        return new Promise((resolve, reject) => {
            // ....
        })
            .then((res) => {
                return {
                    status: 'success',
                    value: res
                }
            })
            .catch((err) => {
                return {
                    status: 'fail',
                    value: err
                }
            })
    }
    //          !
    task()
        .then((result) => {
            console.log(result)
        })
    

    catchでエラーを報告しても後でキャプチャを続けることができます.catchもpromiseオブジェクトを返すからですか.
    Promise.reject('first error')
        .catch((err) => {
            console.log(err)
            throw new Error('second error')
        })
        .then(null, (err) => {
            console.log(err)
        })

    三、await:新しい文法糖
    awaitは非同期コードを同期コードのようにし、シリアルの非同期呼び出しをより自然に書く.awaitの後ろに値またはpromiseオブジェクトがあり、値の場合は直接戻ります.promiseオブジェクトの場合、promiseオブジェクトが成功した後の戻り値を受け入れます.またはawaitの後にasync関数を呼び出す
    const task = async () => {
        return new Promise((resolve, reject) => {
            resolve(1)
        })
    }
    const handle = async () => {
        let value = await task()
        console.log(value)
    }
    handle() // 1

    awaitを使用するには、awaitが成功にのみ関心を持っているため、エラーのキャプチャに注意してください.
    const task = async () => {
        return new Promise((resolve, reject) => {
            reject(1)
        })
    }
    const handle = async () => {
        let value = await task()
        console.log(value)
    }
    handle()

    このように書くとエラーが報告されるので、task関数でエラーをキャプチャするか、task呼び出し時にキャプチャします.
    const task = async () => {
        return new Promise((resolve, reject) => {
            reject(1)
        })
    }
    const handle = async () => {
        let value = await task().catch((err) => { console.log(err) })
        console.log(value) // undefine,             ,          ,      value  
    }

    awaitを使用すると、コードが非同期になります.
    const handle = async () => {
        let value = await 1
        console.log(value)
    }
    handle()
    console.log(2)
    // 2
    // 1

    完~