Chung-Yang Ric Huang
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: Promise / fetch / Async-Await (05/09) tags: 107-2 Ric's Web Programming slideOptions: theme: beige transition: 'fade' slidenumber: true # parallaxBackgroundImage: 'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg' --- ### Promise / fetch / Async-Await ![](https://i.imgur.com/2WlXLO0.png) Spring 2019 。 Ric Huang --- ### Synchronous or Asynchronous? * 大部分的程式語言都是 synchronous (同步) 的在執行,也就是說,在同一個執行緒裡面的指令是依序的被執行,在前面一個指令執行結束之前,下一個指令是被 block 住的,必須等到前一個指令完成之後,下一個指令才會被執行。 * 例如,在 C/C++ 的程式裡當它在忙著做運算或 I/O 的時候,這時候輸入任何東西它是不理你的 ---- ### Synchronous or Asynchronous? * 但在許多的應用,尤其像是在網路的時代,如果一個網頁開啟的時候需要載入一些圖片、影片,你會期待網頁還是會先 show 出來,而只有部分圖片/影片的地方會 load 一下才出來。你不會希望看到的頁面是一片慘白,而必須等到所有圖片、影片都 load 好之後,頁面再 show 出來 ---- ### Asynchronous Methods/Functions * 我們需要非同步(asynchronous)的執行程序,讓一些較花時間的指令被呼叫時,可以以 non-blocking 的方式執行,也就是說讓程式可以當下就把執行的控制權要回來(以繼續執行下一個指令),並且 branch 出另外的執行緒來執行這個較花時間的指令,而且雙方約定好一個通知的方式 (e.g. callback function),讓對方完成的時候(不管是成功或是失敗),本來的程式都會立即知道 * 但 "polling" 通常不會是解決非同步程序的選項 ---- ### 概念上像這樣... ![](https://i.imgur.com/P7m7Dop.png) --- ### 所以, ### JavaScript 是同步還是非同步? ---- * 這樣問其實不精確。JavaScript,如同其他 programming language 一樣,都是同步/blocking 在執行的,但它提供了 non-blocking 的非同步 I/O methods 讓程式在進行 I/O 動作時,得以不會因為 I/O 的速度較慢而影響整體的使用者體驗 * 非同步 I/O 例如:AJAX, WebSocket, DB access, 'fs' module in node.js ---- ### Try this... // Click when start executing. // See how the message is printed. ```javascript function waitThreeSeconds(){ var ms = 3000 + new Date().getTime(); while(new Date() < ms) {} console.log("finished function"); } function clickHandler() { console.log("click event!"); } document.addEventListener('click', clickHandler); console.log("started execution"); waitThreeSeconds(); console.log("finished execution"); ``` => JavaScript executes synchronously. ---- ### Small tip: 善用 function hoist 有沒有看起來不一樣? ```javascript document.addEventListener('click', clickHandler); console.log("started execution"); waitThreeSeconds(); console.log("finished execution"); function waitThreeSeconds(){ var ms = 3000 + new Date().getTime(); while(new Date() < ms) {} console.log("finished function"); } function clickHandler() { console.log("click event!"); } ``` --- ### Recall: Callback Functions * 定義一個 callback function f(), 並且作為參數傳入 g(), 讓我們的程式在執行到 g() 的時候,可以不會因為 g() 的執行時間太久而停下來 ```javascript let url = 'https://some.web.address.com'; let error, response, result; console.log("start execution"); const result = someAsyncFunction(url, callbackFunction); console.log("someAsyncFunction is called"); // When "someAsyncFunction()" is called, // "callbackFunction()" is put in an "event loop" // and gets executed once someAsyncFunction is done function callbackFunction(error, response) { if (error) { /* handle error here */ } else { /* put some info in the response */ } } ``` ---- ### Callback Hell * This kind of callback mechanism is nortorious for the potential "callback hell" situation * A requests B; while waiting for B, do something * B requests C; while waiting for C, do something * C requests D... * Process C's result. Determine success or fail. * Process B's result. Determine success or fail. ---- ### 像這樣... [![](https://i.imgur.com/VrE4anl.png)](http://blog.mclain.ca/assets/images/callbackhell.png) ---- ### To prevent "callback hell" ([ref](http://callbackhell.com/)) 1. Keep your code shallow 2. Modularize your code 3. Handle every single error ---- * This example uses browser-request to make an AJAX request to a server. However, this style may goes into deep callback hell. ```javascript var form = document.querySelector('form'); form.onsubmit = function (submitEvent) { var name = document.querySelector('input').value; request({ uri: "http://example.com/upload", body: name, method: "POST" }, function (err, response, body) { var statusMessage = document.querySelector('.status'); if (err) return (statusMessage.value = err); statusMessage.value = body; } ) } ``` ---- ### 1. Keep your code shallow * To fix it, first name the functions and take advantage of function hoisting to keep the code shallow. ```javascript document.querySelector('form').onsubmit = formSubmit function formSubmit (submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, method: "POST" }, postResponse) } function postResponse (err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body } ``` ---- ### 2. Modularize your code * Secondly, create JavaScript modules — by re-organizing functions into smaller files. * The above example can be simplified as below when the callback functions are defined in another files. ```javascript var formUploader = require('formuploader'); document.querySelector('form').onsubmit = formUploader.submit; ``` ---- // “formuploader.js” is where the modules are defined. ```javascript module.exports.submit = formSubmit function formSubmit (submitEvent) { var name = document.querySelector('input').value request({ uri: "http://example.com/upload", body: name, method: "POST" }, postResponse) } function postResponse (err, response, body) { var statusMessage = document.querySelector('.status') if (err) return statusMessage.value = err statusMessage.value = body } ``` ---- ### 3. Handle every single error * Note: When dealing with callbacks, you are by definition dealing with tasks that get dispatched, go off and do something in the background, and then complete successfully or abort due to failure. * What happens if any of them encounters errors? What happens if you don’t handle the error and continue executing the callback functions? ---- // Important: Apply the Node.js style where the first argument to the callback is always reserved for an error. ```javascript var fs = require('fs'); fs.readFile('/Does/not/exist', handleFile); function handleFile (error, file) { if (error) return console.error('Uhoh, there was an error', error); // otherwise, continue on and use `file` in your code } ``` --- ### Introducing **Promise** * 傳統用 callback 來作為 asynchronous functions 的回應方法很容易讓 code 變得很混亂、破碎,且不容易將不同非同步事件之間的關係描述得很乾淨 * **“Promise”** 就是為了讓在 JavaScript 裡頭的 asynchronous 可以 handle 的比較乾淨 ---- * **Promise 物件**:定義好一個 asynchronous function 執行之後成功或是失敗的狀態處理 * 使用情境:將 Promise 物件的 **.then()**, **.catch()** 加入此 asynchronous function 應該要被呼叫的地方 * Note: ES6 已經把 promise.js 納入標準 ---- * 想像一下你是個孩子,你媽承諾(Promise)你下個禮拜會送你一隻新手機(some async event)。 * 現在你並不知道下個禮拜你會不會拿到手機。你媽可能真的買了新手機給你,或者因為你惹她不開心而取消了這個承諾 (resolved, or error) * 為了把這段人生中小小的事件定義好(所以你可以繼續專心的生活),你將問了媽媽以後會發生的各種情況寫成一個 JavaScript function: ```javascript var askMom = function willIGetNewPhone() { … } askMom(); // execution will be blocked for a week... ``` <small>ref: https://andyyou.github.io/2017/06/27/js-promise/</small> ---- * 基本上一個像是 willIGetNewPhone() 的非同步事件會有三種狀態: * Pending: 未發生、等待的狀態。到下週前,你還不知道這件事會怎樣。 * Resolved 完成/履行承諾。你媽真的買了手機給你。 * Rejected 拒絕承諾。沒收到手機,因為你惹她不開心而取消了這個承諾。 * 我們將把 willIGetNewPhone() 改宣告一個 Promise 物件,來定義上面三種狀態。 ---- ```javascript var isMomHappy = true; var willIGetNewPhone = new Promise(function (resolve, reject) { if (isMomHappy) { var phone = { id: 123 }; // your dream phone resolve(phone); } else { var reason = new Error('Mom is unhappy'); reject(reason); } }); ``` ---- * 定義 Promise 物件的語法: * new Promise(function (resolve, reject) { … }); * 如果一個 Promise 執行成功要在內部 function 呼叫 resolve (成功結果),如果結果是失敗則呼叫 reject (失敗結果)。 * 一個 Promise 物件表達的是一件非同步的操作最終的結果,可以是成功或失敗。 ---- * 用 Promise 定義好 willIGetNewPhone() 之後,我們就可以來改寫 askMom() — ```javascript var askMom = function () { willIGetNewPhone .then(function(fullfilled) { …}) // fullfilled = phone .catch(function(error) { …}); // error = reason }; askMom(); ``` * 在 .then 中使用 function(fullfilled){},而這個 fullfilled 就是從 Promise 的 resolve(成功結果) 傳來的結果 (phone) * 在 .catch 中我們使用了 function(error) {}。而這個 error 就是從 Promise 的 reject(失敗結果) 傳來的 (reason) ---- ### Putting Things Together * 把 asynchronous function 包成 Promise 物件,用 .then() 與 .catch() 來處理成功與失敗的結果。 ```javascript const handleResolved = (data) => {...} const handleRejected = (err) => {...} let myPromise = new Promise((resolve, reject) => { try { const data = getData(); resolve(data); } catch (err) { reject(err); } }); myPromise .then(handleResolved(data)) .catch(handleRejected(err)); ``` ---- ### Chain of Promises * 假設有一連串的 asynchronous functions 要執行,可以把他們的 Promise 串聯起來 — ```javascript doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => {...}) .catch(failureCallback); ``` * 其中 doSomething(), doSomethingElse(), doThirdThing() 就是用 Promise 包起來的三個 asynchronous functions ---- ### .catch() 後面還是可以接東西 * See this example — ```javascript new Promise((resolve, reject) => { console.log('Initial'); resolve(); }).then(() => { throw new Error('Something failed'); console.log('Do this'); }).catch(() => { console.log('Do that'); }).then(() => { console.log('Do this whatever happened before'); }); ``` * Output — ``` Initial Do that Do this whatever happened before ``` ---- ### Promise.all() * p1 & p2 都要成功 ```javascript const p1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, "one"); }); const p2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, "two"); }); Promise.all([p1, p2]).then((value) => { console.log(value); }); ``` * Output: Array [ "one", "two" ] ---- ### Promise.race() * 看誰先成功 ```javascript const p1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, "one"); }); const p2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, "two"); }); Promise.race([p1, p2]).then((value) => { console.log(value); // Both resolve, but p2 is faster }); ``` * Output: two ---- ### Promise 好好學,寫 js 沒煩惱。 --- ### 一個很有用的 Promise — fetch() ([ref](https://andyyou.github.io/2017/06/27/js-promise/)) * 有聽過/用過 XMLHttpRequest() 嗎? * 在 IE7 (2006) 年就有的 API * 命名有些問題 * API 太過低階 ```javascript function reqListener () { console.log(this.responseText); } var oReq = new XMLHttpRequest(); oReq.addEventListener("load", reqListener); oReq.open("GET", "http://www.example.org/example.txt"); oReq.send(); ``` ---- ### 用 fetch() 取代 XMLHttpRequest() —> 回傳 Promise() ```javascript fetch("https://www.google.com") .then((res)=>console.log(res.status)) .catch((err) => console.log('Error :', err)); // output: 200 ``` * 常用的 res.text() res.json() 都是回傳Promise() ```javascript fetch("https://www.google.com") .then((res)=>console.log(res.text())) .catch((err) => console.log('Error :', err)); // output a Promise() ``` ---- ### 用 fetch() 送 POST ```javascript // fetch(url, object) fetch(url, { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Hubot', login: 'hubot' }) }); // method: 可以是 get, post, put, delete // headers: HTTP request header // body: JSON.stringify 轉成 JSON 字串 ``` --- ### From Promise to Async/Await [![](https://i.imgur.com/sL7ItUE.png)](https://medium.com/@peterchang_82818/javascript-es7-async-await-%E6%95%99%E5%AD%B8-703473854f29-tutorial-example-703473854f29) --- * 雖然 ES7 原生支援 async/await, 但每個瀏覽器的支援程度不同,使用前還是要先查一下,否則就要用 Babel. [![](https://i.imgur.com/QnMFUKB.png)](https://caniuse.com/#search=async) ---- ### Promise vs. Async/Await * Promise ```javascript Doctor.findById() .then(doctor => { // some logic doctor... return somePromise; }).then(...); ``` * Async/Await ```javascript const f = async () => { const doctor = await Doctor.findById(); // some logic doctor... } f(); ``` ---- ### Promise vs. Async/Await * Async/Await 的目的在讓 Promise 概念的使用更直覺 * 在 top-level function 前面加個 async ```javascript async function name([param[, param[, ... param]]]) { statements } ``` * 用 await 把原先非同步結束後才要在 .then() 執行的程式碼直接隔開,平行的放到下面 ```javascript async function name([param[, param[, ... param]]]) { let ret = await doSomethingTakesTime(); // this part does not run until await finishes } ``` * 基本上 async 匡了一個 top-level async 的區域,然後裡面透過 await 變成是 synchronous ---- #### Promise vs. Async/Await ([ref](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/async_function)) ![](https://i.imgur.com/pT4PcfS.png) ![](https://i.imgur.com/VnguAhf.png) ---- * Await sequentially ![](https://i.imgur.com/RiQbGhv.png) * Await parallelly ![](https://i.imgur.com/rn7Y4bh.png) ---- * Nested Await ![](https://i.imgur.com/C8ZQqvm.png) --- ### 關於期中 Project ([ref](https://hackmd.io/SABySOxaQH-r_HODiPvnRA)) ![](https://i.imgur.com/ULZ2GMD.png) ---- ### General Rules * 題目自訂,一個人一組, 佔學期成績 20% * Requirements * Client-side programs in React.js * Server-side programs in Node.js * Recommended * Database to store persistent data * Use "Promise" or "Async/Await" to ensure asynchronous non-blocking communications * Deadline: 9pm, Tuesday, 05/21 ---- ### 關於 Github Repo * 建立一個 public 的 repo (平行於 **WebProg2019**),命名為:**midterm_\<yourAppName>** * 請確認可以使用 "npm install" 安裝你的 app, 並且可以使用 "npm start" 執行你的 app * 如有其他的 scripts 或者是執行時的注意事項, 請在 README.md 中描述清楚 * Project 的目錄與檔案命名名稱不限,但請盡量 follow 上課所講的 conventions (e.g. public, src, containers, components) * Optional: Deploy 到 cloud 並提供固定網址以利他人操作測試 ---- ### 關於使用他人的框架與模組 * 歡迎使用 React/Node family 相關的 framework (e.g. Next.js),現代化的 web programming 不鼓勵土法煉鋼,多多利用別人開發好的套件/工具,才能達到事半功倍的效果 * 但為了收斂大家的學習,也確保大家有把我們教的 React/Node 真正的趁這個 project 好好複習一下,所以請不要使用別的框架 (e.g. Angular.js, PhP) * 也歡迎參考別人的 open source 專案,在他的上面去開發你的應用 * 但請在 README.md 裏頭講清楚你的修改與貢獻 ---- ### README.md * 請在 Project 的根目錄編輯一份 README.md,以利助教快速了解你的 project 內容 * 內容應至少包含: * 題目名稱 * 一句話描述這個 project 在做什麼 * (Optional) Deployed 連結 * 使用/操作方式 * 其他說明 * 使用與參考之框架/模組/原始碼 * 我的貢獻 * 心得 ---- ### 繳交注意事項 * 請在 deadline 以前將 repo 連結 PO 至 FB 社團 ([link](https://www.facebook.com/groups/NTURicWebProg/)),發文時請將 README.md 的內容複製貼至 PO 文 * PO 文至 FB 社團即算完成繳交,但請勿在未完成之前 PO 文,以免助教下載到屍體版本 * 評分標準:題目深度(30%)、完成度(40%)、Coding 品質(20%)、是否符合規定(10%) --- ### That's it!! ### Enjoy hacking the project!!

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully