iT邦幫忙

0

Promise實際操演

Promise

JavaScript 是屬於同步的程式語言,因此一次僅能做一件事情,但遇到非同步的事件時,就會將非同步的事件移動到程式碼的最後方,等到所有的原始碼運行完以後才會執行非同步的事件。

以下列的程式碼來說,在 console 中依序的會出現的順序為:

  1. 開始
  2. 結束
  3. 非同步事件 <- 最後執行
console.log('開始');

setTimeout(() => {
  console.log('非同步事件');
}, 0);

console.log('結束');

雖然在上段的原始碼中,setTimeout 所定義的時間為 0,但因為是屬於非同步事件,因此還是會在其他原始碼運行完以後才執行。

我們再axios文件中找一段實際範例參考,先定義一個data物件,中間段落使用 axios 嘗試取得遠端資料,後面的緊接的 console.log(data),實際測試看看console的結果是什麼?

你應該會覺得concole順序依序為:

  1. "開始執行"
  2. '/user?ID=12345
  3. "測試有沒有接收到資料"
let data = {}

console.log('開始執行');

const api = '/user?ID=12345'
axios.get(api).then(response => {
  data = response;
  console.log('測試有沒有接收到資料');
});

console.log(data);

其實正確的 console 中依序的會出現的順序是:

  1. "開始執行"
  2. [object Object] { ... }
  3. "測試有沒有接收到資料"

你應該會想說為什麼console.log(data)會沒有接收到資料呢?
因為上述的非同步行為,導致api還尚未接收到,因此才會出現[object]

實戰演練Promise,以Vue為範例

一、數字題型:

const app = new Vue({
  el: '#app',
  created() {
    this.promise(100)
      .then(success => {
        console.log(success);
        return this.promise(1000);
      })
      .then(success => {
        console.log(success);
        return this.promise(0); // 這個階段會進入 catch
      })
      .then(success => {   // 由於上一個階段結果是 reject,所以此段不執行
        console.log(success);
        return this.promise('Hello');
      })
      .catch(error => {
        console.log(error);
      })
  },
  methods: {
    promise (num) {
      return new Promise((resolve, reject) => {
        num ? resolve('成功') : reject('失敗');
      });
     }
   },
 })

因此console出來的結果為:

  1. 成功
  2. 成功
  3. 失敗

二、JSON Data一般題型:
將題型一的值更改為JSON,分別為apiA以及apiB
可將此段程式碼複製貼到開發者工具看有沒有什麼不同的地方~

const app = new Vue({
  el: '#app',
    created () {
    const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire'
    const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire'
    this.demo(apiA).then(res => {
      console.log(res)
      return this.demo(apiB)
    }).then(res => {
      console.log(res)
    }).catch(err => {
      console.log(err)
    })
  },
  methods: {
    demo (api) {
      return new Promise((resolve, reject) => {
        axios.get(api).then((res) => {
          const data = res.data.result.results
          res.status === 200 ? resolve(data) : reject('error api')
        })
      })
    }
  }
})

三、進階題型:
使用indexOf去尋找哪個api中有符合80ab7634的字串,有的話則會執行setTimeout延遲5秒!
然而,在這個範例中indexOf尋找到apiA有符合,但是js是由上到下去執行的,因此js會等待apiA回傳回來後,接著馬上跟著回傳apiB,因此apiA與apiB都將延遲5秒回傳

const app = new Vue({
  el: '#app',
  created() {
    const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire'
    const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire'
    this.demo(apiA).then(res => {
      console.log(res);
      return this.demo(apiB)
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
  },
  methods: {
    demo(api) {
      return new Promise((resolve, reject) => {
        axios.get(api).then((res) => {
          if (res.status === 200) {
            if (api.indexOf('80ab7634') > -1) {
              setTimeout(() => {
                resolve(res.data)
              }, 5000)
            } else {
              resolve(res.data)
            }
          } else {
            reject('error api')
          }
        })
      })
    },
  },
})

假如我們將indexOf的值變更為36847f3f,猜猜看會出現什麼有趣的事呢?
這時會發現,js先回傳apiA,經過了5秒後才會回傳apiB
因為js未搜尋到apiA有36847f3f的值,因此不會去執行setTimeOut

const app = new Vue({
  el: '#app',
  created() {
    const apiA = 'https://data.taipei/api/v1/dataset/80ab7634-dabd-4012-be8c-fb7da0101e9b?scope=resourceAquire'
    const apiB = 'https://data.taipei/api/v1/dataset/36847f3f-deff-4183-a5bb-800737591de5?scope=resourceAquire'
    this.demo(apiA).then(res => {
      console.log(res);
      return this.demo(apiB)
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
  },
  methods: {
    demo(api) {
      return new Promise((resolve, reject) => {
        axios.get(api).then((res) => {
          if (res.status === 200) {
            if (api.indexOf('36847f3f') > -1) {
              setTimeout(() => {
                resolve(res.data)
              }, 5000)
            } else {
              resolve(res.data)
            }
          } else {
            reject('error api')
          }
        })
      })
    },
  },
})

希望藉由上述三個不同的簡單實例,能讓大家更了解Promise的運作模式


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

我要留言

立即登入留言