iT邦幫忙

1

在axios使用promise

  • 分享至 

  • xImage

我的程式碼大概長這樣, 請問應該怎樣才能使console.log(rawData)可以等待上面的setTimeout呢? 謝謝

axios
	.get(BASE_URL)
	.then((response) => {
		setTimeout(() => {
			rawData = response.data
		}, 3000)
	})
	.then((response) => {
		console.log(rawData)
	})
	.catch((err) => console.log(err));
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

5
cloudeasy
iT邦新手 5 級 ‧ 2022-03-03 20:54:01
最佳解答

axios 使用 Javascript 的 Promise

給一個範例:

const promise = new Promise((resolve) => {
  return resolve('abc')  // 將abc字串丟到下一個 then 
}).then((data) => {
  console.log(data) // data 收到 'abc'
})

// abc

使用以上的概念,在你的 setTimeout 裡面使用 Promise.resolve(data)

axios
	.get(BASE_URL)
	.then((response) => {
        return new Promise( (resolve) => {
            setTimeout(() => {
                resolve(response.data) 
                // 3秒後透過Promise.resolve(response.data) 丟到下一個 then 
            }, 3000)
        } )
		
	})
	.then((rawData) => { // 接收上一個resolve丟過來的資料
		console.log(rawData)
	})
	.catch((err) => console.log(err));
看更多先前的回應...收起先前的回應...
hypons iT邦新手 5 級 ‧ 2022-03-03 21:31:30 檢舉

我剛在家測試完,成功了~
謝謝你~

不過我一直以為axios的.then是會等待上一步完成後才會執行....不用再另外new一個promise

認真來說,你的理解是正確的。
它的確是等待上一步完成才執行。

但你誤解的是它完成的定義。

在它執行了setTimeout。對它來說就是已經完成了。
至於setTimeout等3秒後執行的程式。與它無關。
因為那又是另一個閉包的事了。

對它來講,它已經完成了「呼叫setTimeout」

你要理解JS的同步及非同步是什麼才行。

hypons iT邦新手 5 級 ‧ 2022-03-04 09:58:25 檢舉

聽了你這樣說, 我想我下面的理解應該是錯的了

非同步是把要執行的指令放在stack中, 然後不管了, 直接執行下一行指令.
同步是要完成每一個指令才執下一個指令.

所以第二個.then是怎樣拿到rawData的呢?

不好意思我真的很不熟處理非同步

Todd iT邦新手 1 級 ‧ 2022-03-04 10:53:30 檢舉

當你執行setTimeout時,就會開始在瀏覽器web apis開始讀秒,直到時間到了才會將setTimeout的callback放進event loop中的 callback queue
然後直到call stack清空才會將 queue裡的callback function放進去並開始執行。

所以「大概」的流程會是

  1. axios.get放入到callback queue
  2. fulfilled後執行.then()
  3. 執行 setTimeout 註冊timer
  4. 執行下一個.then()
  5. 執行console.log(rawData)
  6. timer計數完畢將setTimeout的callback放進queue
  7. 執行rawData = response.data

問題就是會在第三步只是單純的執行setTimeout,所以基本上會像是.then(response=>void).then()
那第二個.then沒有什麼東西可以等待的

hypons iT邦新手 5 級 ‧ 2022-03-05 16:20:36 檢舉

所以如果不是用setTimeout 而是資料太多要.get很久的話。
結果也是 .then(response=>void).then() 嗎?

題外問一下,你們覺得當後端工程師也有需要熟悉處理非同步嗎?
因為其實我是在讀後端....

Todd iT邦新手 1 級 ‧ 2022-03-07 23:22:33 檢舉

hypons
不會,並不是setTimeout等太久所以最後是void
而是setTimoue本身就沒有retune value,然後剛好是非同步行為
所以他會等到同步的行為(確切來說是 call stack清空)
才會執行到setTimeout的callback
所以.then(response=>void).then()中第二個.then 才不會等待setTimeout執行完才執行裡面的console.log,而是馬上執行

axios.get()本身就是會回傳一個promise 所以你才有辦法.then取出她的值,所以如果你又要進行一個有順序/需要等待的非同步操作,你就得用另外一個promise包起來
也就是上面例子在setTimeout裡時間到後將值resolve,而且也要回傳整個promise,否則下一個.then會不知道到底是在等誰

.get太久可能會發生其他事情,像是timeout之類的錯誤,在promise中會用.catch去做錯誤處理

至於後端需不需要懂非同步的問題,看語言
因為不是每個語言都有非同步的設計

cloudeasy iT邦新手 5 級 ‧ 2022-03-08 18:18:35 檢舉

可以單純看一個正常 function 的表現

function demo () {
  setTimeout(() => {
   console.log('timed out')
  }, 2000)
  return 'done'
}

console.log(demo())

return 的時候表示 function 結束
可以清楚觀察到 console.log 先印出 done, 約2秒後才印出 timed out

在執行的時候 setTimeout 會先被放在背景的 task queue 中, 直接執行下一行 return 'done'

這就是為什麼你認為 function 會等待裡面的程式全部做完才會結束
但 function 其實早在 setTimedout 的 callback 執行前就已經結束了, 放到背景queue的 task 已經與function脫離獨立

至於背景task怎麼運作的有興趣可以看一下
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

hypons iT邦新手 5 級 ‧ 2022-03-11 13:20:54 檢舉

Todd
所以你意思是如果情況單純是想等.get把全部資料都取得後才.then動態的加到畫面中,是不需要再加promise也可以做到。但因為我發問時的例子用了setTimeout,所以才需要另外再加一個promise。是這樣嗎?

hypons iT邦新手 5 級 ‧ 2022-03-11 13:22:22 檢舉

cloudeasy
哦, 那我懂了, 所以對.then來說是已經完成執行. 但setTimeout其實是卡在task queue...

我要發表回答

立即登入回答