結合 async 非同步函式 - 提高 Promise 的易用性

Pandaaa發表於2019-03-21

前言

非同步函式怎麼工作的?

  • 開局一張圖
    image
async function myAsyncFunc(){
    try {
        const fulfilledValue = await promise
    }catch(rejectValue){
        console.error('error:', rejectValue)
    }
}
複製程式碼

函式定義之前使用了 async 關鍵字,就可以在函式內使用 await。 當您 await 某個 Promise 時,函式暫停執行,直至該 Promise 產生結果,並且暫停並不會阻塞主執行緒。 如果 Promise 執行,則會返回值。 如果 Promise 拒絕,則會丟擲拒絕的值。

如何用我們的 async 改寫我們的 promise 程式碼

  • 假如我們這裡需要獲取一段文字資料
function logFetch(url) {
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      console.log(text);
    }).catch(err => {
      console.error('fetch failed', err);
    });
}
複製程式碼
  • 下面用 async 非同步函式改寫
async function logFetch(url) {
  try {
    const response = await fetch(url);
    
    // 列印成功獲取的資料
    console.log(await response.text());
  }
  catch (err) {
  
    // 同樣的丟擲錯誤
    console.log('fetch failed', err);
  }
}
複製程式碼

去掉了萬惡的 return 回撥函式,是不是程式碼清爽很多了。

非同步函式返回值

  • 無論是否使用 await,非同步函式都會返回 Promise。該 Promise 解析時返回非同步函式返回的任何值,拒絕時返回非同步函式丟擲的任何值。

因此,對於:

// wait ms milliseconds
function wait(ms) {
  return new Promise(r => setTimeout(r, ms));
}

async function hello() {
  await wait(500);
  return 'world';
}
複製程式碼

…呼叫 hello() 返回的 Promise 會在執行時返回 "world"。

async function foo() {
  await wait(500);
  throw Error('bar');
}
複製程式碼

…呼叫 foo() 返回的 Promise 會在拒絕時返回 Error('bar')。

如果我們想按照順序獲取資料啦?

  • 直接使用 promise
function logInOrder(urls) {
  // 先使用我們上面寫好的 fetch 函式獲取所有的資料
  const textPromises = urls.map(url => {
    return fetch(url).then(response => response.text());
  });

  // 然後用 reduce 把前面的 promises 一一的進行處理
  textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  }, Promise.resolve());
}
複製程式碼
  • 如果使用 async 的話
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
複製程式碼

這樣是不是簡潔很多,但是這樣的話我們的第二次獲取資料要在第一次資料獲取完畢才能開始,這樣就犧牲了效能,但是我們還有更好的方法

async function logInOrder(urls) {
  // 使用 map,和 async 改寫,這樣可以並行獲取資料
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
複製程式碼

上面的程式碼解決了我們並行獲取資料的時間問題,又能按照我麼你的需求一一按順序列印我們的資料

使用其他語法

  • 箭頭函式
const mySync = async url=> {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.log('fetch failed', err);
  }
}
複製程式碼
  • 物件方法
const storage = {
  async getAvatar(name) {
    const cache = await caches.open('avatars');
    return cache;
  }
};

storage.getAvatar('jaffathecake').then(…);
複製程式碼
  • 如果情況複雜你還可以使用類來改寫
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
複製程式碼

參考

相關文章