在巴哈姆特上學到的。
原理簡述
其實原理應該就是 async
await
的語法,只要建立一個 Promise
就可以用 await 來強制等 Promise 執行完才執行後面的程式碼,換句話說就是強制把「非同步 -> 同步」的感覺吧?
這一塊我還沒學到,所以只是我大概瞎猜的,但沒關係就先記下來吧!
備註:await
必須包在 async
開頭的 function 裡面才能用,意思就是「這個 function 裡有非同步 function 的意思吧!」。
備註:await
後面只要接 Promise,就會等這個 Promise 執行 resolve
或 reject
完才會往下執行。
備註:我很常會把 resolve
放錯地方。記住一個要訣:resolve
一定要放在非同步執行完的那個「 callback」裡面呼叫,這樣子才會等非同步執行後再往下跑。
補充-async 不是你想的那樣
注意 async
指的是「裡面」有非同步 function,不是「function 自己」是非同步函式,把下面兩段拿去跑你就懂了。
不是非同步:
1 2 3 4 5 6 7 8 9
| async function fakeAsync() { console.log('fake asnyc!!!') } for(let i=0; i<=100; i++) { fakeAsync() console.log('sync!! \n') }
|
是非同步:
1 2 3 4 5 6 7 8 9
| async function asyncFunction() { console.log('asnyc!!!') } for(let i=0; i<=100; i++) { setTimeout(asyncFunction, 0) console.log('sync!! \n') }
|
範例一 更新變數的值
一般的情況:
1 2 3 4 5 6 7 8 9
| ~async function() { let a = 10 setTimeout(function() { a = 100 }, 1000) console.log(a) }()
|
利用 Promise
+ await
:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ~async function() { let a = 10 await new Promise((resolve, reject) => { setTimeout(function() { a = 100 resolve() }, 1000) }) console.log(a) }()
|
範例二 讓迴圈的速度變成 LKK
年輕版(錯誤示範):
1 2 3 4 5 6 7 8 9 10
| ~async function() { for (var i=1; i<=5; i++) { setTimeout(function() { console.log(i) }, 1000) } }()
|
老年版:
1 2 3 4 5 6 7 8 9 10 11 12
| ~async function() { for (let i=0; i<=10; i++) { await new Promise((resolve, reject) => { setTimeout(function() { resolve() }, 1000) }) console.log(i) } }()
|
範例三 發送 request
傳說中的 callback hell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const request = require('request') request('https://reqres.in/api/users/1', function(err, res, body) { console.log('拿到第一筆資料了!', JSON.parse(body)) request('https://reqres.in/api/users/2', function(err, res, body) { console.log('拿到第二筆資料了!', JSON.parse(body)) request('https://reqres.in/api/users/3', function(err, res, body) { console.log('拿到第三筆資料了!', JSON.parse(body)) request('https://reqres.in/api/users/4', function(err, res, body) { console.log('拿到第四筆資料了!', JSON.parse(body)) }) }) }) })
|
改用 Promise
+ async
:
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 33 34
| ~async function() { await new Promise((resolve, reject) => { request('https://reqres.in/api/users/1', (err, res, body) => { console.log('拿到第一筆資料了!', JSON.parse(body)) resolve() }) }) await new Promise((resolve, reject) => { request('https://reqres.in/api/users/2', (err, res, body) => { console.log('拿到第二筆資料了!', JSON.parse(body)) resolve() }) }) await new Promise((resolve, reject) => { request('https://reqres.in/api/users/3', (err, res, body) => { console.log('拿到第三筆資料了!', JSON.parse(body)) resolve() }) }) await new Promise((resolve, reject) => { request('https://reqres.in/api/users/4', (err, res, body) => { console.log('拿到第四筆資料了!', JSON.parse(body)) resolve() }) }) }()
|
同場加映 Promise 的原本用法
暫時先放這吧!不然我也不知道該放哪才好~
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
| function getUser(id) { return new Promise((resolve, reject) => { request(`https://reqres.in/api/users/${id}`, (err, res, body) => { if (body) { resolve(body) } else { reject(err) } }) }) }
getUser(1) .then((data) => { console.log('第一筆資料!' ,JSON.parse(data)) return getUser(2) }) .then((data) => { console.log('第二筆資料!', JSON.parse(data)) return getUser(3) }) .then((data) => { console.log('第三筆資料!', JSON.parse(data)) }) .catch((err) => console.log('err:', err)) .catch((err) => console.log('err:', err)) .catch((err) => console.log('err:', err))
|
同場加映 async 的原本用法
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 33 34 35 36 37
| function getUser(id) { return new Promise((resolve, reject) => { request(`https://reqres.in/api/users/${id}`, (err, res, body) => { if (body) { resolve(body) } else { reject(err) } }) }) } ~async function() {
{ const json = await getUser(1) const { data } = JSON.parse(json) console.log(data) } { const json = await getUser(2) const { data } = JSON.parse(json) console.log(data) } { const json = await getUser(3) const { data } = JSON.parse(json) console.log(data) } }()
|
或搭配迴圈寫的更簡潔:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function getUser(id) { return new Promise((resolve, reject) => { request(`https://reqres.in/api/users/${id}`, (err, res, body) => { if (body) { resolve(body) } else { reject(err) } }) }) } ~async function() { for (let i=1; i<=3; i++) { const json = await getUser(i) const { data } = JSON.parse(json) console.log(i, data) } }()
|
同場加映 async 的回傳值也是一個 Promise
只要回傳的是 Promise,就可以一直 then
下去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| async function user1() { const json = await getUser(1) console.log({ data } = JSON.parse(json)) }
user1() .then(async() => { const json = await getUser(2) console.log({ data } = JSON.parse(json)) }) .then(async() => { const json = await getUser(3) console.log({ data } = JSON.parse(json)) })
|
同場加映 比 setTimeout 難一點的 setInterval
希望你能成為非同步操控大師:
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
| function count(msg, ms) { return new Promise((resolve, reject) => { let counter = 0 let timer = setInterval(() => {
counter++ console.log(`${msg}: ${counter}`) if (counter === 5) { clearInterval(timer) resolve() } }, ms) }) }
~async function() { console.log('1') await count('yo', 100) console.log('2') }()
|
耍帥的寫法(其實跟上面一模一樣):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ~async function() { console.log(1) await new Promise((resolve, reject) => { ~function(msg, ms) { let counter = 0 const timer = setInterval(() => { counter++ console.log(`${msg}: ${counter}`) if (counter === 5) { clearInterval(timer) resolve() } }, ms) }('yo', 100) }) console.log(2) }()
|
同場加映 更好懂的排序方式
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 33
| const request = require('request') async function getUser(id) { let json = null await new Promise((resolve) => { request({ url: `https://reqres.in/api/users/${id}`, }, (err, res, body) => { json = body resolve() }) }) return json }
function getAllUsers(user1, user2, user3) { console.log('user1', JSON.parse(user1)) console.log('user2', JSON.parse(user2)) console.log('user3', JSON.parse(user3)) }
~async function() { const user1 = await getUser(1) const user2 = await getUser(2) const user3 = await getUser(3) getAllUsers(user1, user2, user3) }()
|
取得所有資料 Promise.All
當你想一次打多個 API 時,應該就會用到這個方法:
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 33
| let data = { name: 'jim', email: 'jimjim@gmail.com' }
const youtubeAPI = new Promise(resolve => { setTimeout(() => { resolve({ video: [1, 2, 3, 4, 5] }) }, 3000) })
const facebookAPI = new Promise(resolve => { setTimeout(() => { resolve({ user: 'name' }) }, 1000) })
const checkPermission = new Promise((resolve, reject) => { setTimeout(() => { data ? resolve(data) : reject(`We don't find the data.`) }, 2000) })
Promise.all([youtubeAPI, facebookAPI, checkPermission]) .then(result => { console.log(result[0]) console.log(result[1]) console.log(result[2]) }).catch(err => { console.log(err); })
|