Leon's Blogging

Coding blogging for hackers.

JavaScript - HTTP Request, Promise, Async/await

| Comments

Asynchronous(異步/非同步) vs. Synchronous(同步)

  • Asynchronous: 不需等待,可以繼續做別的事
  • Synchronous: 必須等待事情完成,才能繼續做別的事

XMLHttpRequest

1
2
3
4
5
6
7
8
9
10
const request = new XMLHttpRequest()
request.addEventListener('readystatechange', e => {
  // 4 代表已 request 完成,已經有 response
  if (e.target.readyState === 4) {
    const data = JSON.parse(e.target.responseText)
    console.log(data)
  }
})
request.open('GET', 'http://puzzle.mead.io/puzzle')
request.send()

透過 callback function 可以當 response 回來時,在執行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const getPuzzle = callback => {
  const request = new XMLHttpRequest()
  request.addEventListener('readystatechange', e => {
    if (e.target.readyState === 4 && e.target.status === 200) {
      const data = JSON.parse(e.target.responseText)
      callback(undefined, data.puzzle)
    } else if (e.target.readyState === 4) {
      callback('An error has taken place', undefined)
    }
  })
  request.open('GET', 'http://puzzle.mead.io/puzzle?wordCount=3')
  request.send()
}


// callback function
getPuzzle((error, puzzle) => {
  if (error) {
    console.log(`Error: ${error}`)
  } else {
    console.log(puzzle)
  }
})

Promise

簡單的來說,Promise 主要用來處理非同步狀態(async), 就是承諾當任務完成時,通知下一個任務可以開始,不會像上面一樣要一直寫 callback 導致一些 callback hell

Promise 的生命週期

  • pending - 等待中的初始狀態
  • fulfillment - 完成
  • rejecttion - 失敗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Callback
const getDataCallBack = callback => {
  setTimeout(() => {
    callback(undefined, 'This is the callback data')
  }, 2000)
}

getDataCallBack((err, data) => {
  if (err) {
  } else {
    console.log(data)
  }
})

// Promise
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // 成功做什麼事
    resolve('This is the promise resolve')
    // 失敗做什麼事
    reject('This is the promise reject')
  }, 2000)
})

myPromise.then(
  data => {
    console.log(data)
  },
  err => {
    console.log(err)
  }
)

Promise chain

catch 取代 error handler err => {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const getDataPromise = num =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      typeof num === 'number' ? resolve(num * 2) : reject('Number must be provided')
    }, 2000)
  })

getDataPromise(10)
  .then(data => {
    getDataPromise(data)
      .then(data => {
        console.log(data) // 40
      })
      .catch(err => {
        console.log(err)
      })
  })
  .catch(err => {
    console.log(err)
  })

// better way
getDataPromise(10)
  .then(data => {
    return getDataPromise(data)
  })
  .then(data => {
    console.log(data) // 40
  })
  .catch(err => {
    console.log(err)
  })

也有一些非同步模式的變體可以使用

  • Promise.all: 等所有都完成再進行下一步
  • Promise.race: 只要一個先完成就進行下一步

fetch

fetch 會回傳 promise object,因此也可以用 then 去接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fetch = require('node-fetch')

fetch('http://puzzle.mead.io/puzzle', {})
  .then(response => {
    if (response.status === 200) {
      return response.json()
    } else {
      throw new Error('Unable to fetch the puzzle')
    }
  })
  .then(data => {
    console.log(data.puzzle)
  })
  .catch(error => {
    console.log(error)
  })

Async/Await

async function 也會回傳 Promise 的函式

  • async: 定義一個 functionasync
  • await: 等待某一 function return 後再繼續執行,必須包在 async 裡面
1
2
const test = async () => {}
test() // Promise {<resolved>: undefined}
1
2
3
4
5
6
7
8
9
10
11
12
const processData = async () => {
  // throw new Error('error')
  return 'hi'
}

processData()
  .then(data => {
    console.log(data)
  })
  .catch(error => {
    console.log(error)
  })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// node need to require
const fetch = require('node-fetch')

const getPuzzle = async wordCount => {
  const response = await fetch(`http://puzzle.mead.io/puzzle?wordCount=${wordCount}`)
  if (response.status === 200) {
    const data = await response.json()
    return data.puzzle
  } else {
    throw new Error('Unable to get puzzle')
  }
}
getPuzzle('2')
  .then(puzzle => {
    console.log(puzzle)
  })
  .catch(err => {
    console.log(`Error: ${err}`)
  })

Comments