Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

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 🍾🍾🍾