Skip to content

Latest commit

 

History

History

chapter-17

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Chapter 17. 異步操作和 Async 函数

大綱

ㄧ. Blocking and waiting

Blocking (pulling):

function login() {
  const fbUser = fbLogin();
  const user = getCurrentUser(fbUser);
  return user;
}

在 Jafar Husain 的影片(連結)中,他提到的 Pulling 概念可以用下面這一句來想像:
fbUser <- fbLogin()

換成白話文的意思就是 - 我們調用 fbLogin 這個函數,並將 fbUser 的值從中拉 (pull) 出來。

Waiting (pushing):

function login() {
  fbLogin((fbUser) => {
    getCurrentUser(fbUser, (user) => {
    });
  });
}

如果 fbLogin 是非同步的請求(例如 AJAX),我們沒辦法馬上得到資料,也不能讓程式 blocking 在這一行(如果使用者點了登入按鈕,卻看到頁面卡在那裡是會抓狂的)。

所以我們可以使用 Pushing 的概念,用下面這一行想像:
fbLogin( -> fbUser)

白話文 - 我們調用 fbLogin,並將結果值推 (push) 給 fbUser。

二. Callback function

如果瞭解了 blocking 和 waiting 的概念,在 ES6 以前,如果我們要寫 Async 的邏輯,勢必一定要用 callback。

function login(callback) {
  fbLogin((err, fbUser) => {
    if (err) {
      callback(err);
      return;
    }
    getCurrentUser(fbUser, (err, user) => {
      callback(err, user);
    });
  });
}

為了不讓我們的程式停在非同步請求,我們將邏輯拆分成兩段:
一段是調用請求的主程式;
一段是處理請求結果的 callback 函數。

不過使用 Callback 處理 Async 的隱憂就是:

Callback hell

三. ES2015 Promise

到了 ES6,想必你也用過 Promise 來避免 Callback hell:

fbLogin()
  .then(getCurrentUser)
  .then((user) => console.log(user))
  .catch((err) => console.log(err));

四. ES2015 Generator function

ES6 提供了我們另外一個語法糖 - Generator

function* login() {
  const fbUser = yield fbLogin();
  const user = yield getCurrentUser(fbUser);
  return user;
}

這語法糖讓我們可以使用 Pulling 的方式撰寫非同步操作,讓編寫同步和非同步程式的思維一致化。

PS. 如果你把上面那一段程式的 *yield 拿掉,是不是就跟同步邏輯一樣了呢!

使用 spawn

不過 Generator 是一個狀態機,我們必須調用 login 函數後拿到最後 user 的值,而不是停在每一個 yield 的中斷點上;因此我們需要一個執行器。

const promise = spawn(login());

promise
  .then((user) => console.log(user))
  .catch((err) => console.log(err));

spawn.js 幫我們解決了這個問題,我們將 Generator 回傳的 iterator 遞給 spawn,它會幫我們執行每一個非同步操作,直到那到最後的值,並 push 到你的程式中。

spawn.js 是如何執行這些非同步的?

function spawn(iterator) {
  return new Promise((resolve, reject) => {
    const onResult = (data) => {
      const { value, done } = iterator.next(data);
      if (!done) value.then(onResult);
      else resolve(data);
    };
    onResult();
  });
}

不過使用 Generator 來撰寫 push 風格的非同步操作,卻需要額外的執行器(spawn.js);想像以後我們需要專們處理 Async 的語法糖,不再需要使用者些執行器...

五. ES2016 Async function

ES7 的 Async/Await 就是專門解決非同步操作的語法糖,我們不再需要引用額外的程式。

async function login() {
  const fbUser = await fbLogin();
  const user = await getCurrentUser(fbUser);
  return user;
}

login()
  .then((user) => console.log(user))
  .catch((err) => console.log(err));

六. More Async programming

在 Jafar Husain 的影片末也有提到未來 ES Async programming 的走向,例如 Observable interface:

async function getReplies() {
  for (let reply on new WebSocket('/posts/1/replies')) {
    console.log(reply);
  }
}

這部影片值得去看一看,可以了解整個 Async programming 的來龍去脈(連結)!

七. 參考資源

  1. Jafar Husain: Async Programming in ES7 | JSConf US 2015
  2. 异步操作和 Async 函数
  3. Callback Hell - A guide to writing asynchronous JavaScript programs

八. Curators

  • shiningjason: Hope this article will inspire you 🍾🍾🍾