<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Will Wang 王興謙 on Medium]]></title>
        <description><![CDATA[Stories by Will Wang 王興謙 on Medium]]></description>
        <link>https://medium.com/@will.wang?source=rss-f512818fc477------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*SVyFAjfqUcZsvAFj.</url>
            <title>Stories by Will Wang 王興謙 on Medium</title>
            <link>https://medium.com/@will.wang?source=rss-f512818fc477------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 13:06:50 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@will.wang/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[不要踏到 Node.js 的黑洞陷阱  ]]></title>
            <link>https://medium.com/@will.wang/nodejs-and-nodemon-blackhole-2651c7f8307e?source=rss-f512818fc477------2</link>
            <guid isPermaLink="false">https://medium.com/p/2651c7f8307e</guid>
            <category><![CDATA[nodemon]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Will Wang 王興謙]]></dc:creator>
            <pubDate>Sun, 12 May 2019 08:38:59 GMT</pubDate>
            <atom:updated>2019-05-13T03:54:02.771Z</atom:updated>
            <content:encoded><![CDATA[<pre>精確的說是一個 nodemon 的陷阱，讓我 debug 到快崩潰，而且我發現不少人都有碰到。</pre><pre>如果你有在用 nodemon，也許讀過這篇可以節省你很多小時。</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/946/1*xTyBm5IE3rqOH2xV7A8b3g.jpeg" /><figcaption>(網路看到的圖 lol … 實際上遠不只 5 hours)</figcaption></figure><p>(註：<a href="https://github.com/remy/nodemon">nodemon</a> 是一個知名的實用工具，會持續 monitor 你在開發 Node.js 時的 code，一有改動就自動重跑 node。)</p><p>我最近在開發一個 service，會用 Azure Service Bus 的 Topics (<a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">pub/sub</a> 服務) 來傳遞 message：</p><ul><li>收送兩端都是用 Node.js 寫的</li><li>收端有一個 while(1) loop 不斷收 message，收到後會 call foo() function 做事。</li></ul><p>這裡是收端 code 的骨架，有需要的話可以參考：</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7de3939e7f1546019686d498b528e043/href">https://medium.com/media/7de3939e7f1546019686d498b528e043/href</a></iframe><h3>一開始都很好，後來我開始碰到第一層問題：</h3><pre>收端「有時候」完全收不到 message (或只收到少數幾個)</pre><p>於是我做這些事：</p><ol><li>一直檢查 send 的 status code (always 成功)</li><li>檢查 azure (<a href="http://portal.azure.com/">portal</a>) 有沒有收到 (時有時無)</li><li>研究 azure 的 <a href="https://docs.microsoft.com/zh-tw/azure/service-bus-messaging/duplicate-detection">Duplicate detection 機制</a>，包括故意讓 message body 不一樣 (沒用)</li><li>研究 <a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/topic-filters">pub/sub filter</a> rule</li><li>換 pub/sub topic name (沒用)</li><li>某些 string 單引號雙引號互換 ……. (ok 爛透了)</li><li>測試 peek lock (沒用)</li><li>測試最一開始有成功過的官方 example code</li><li>調整收訊息的 function 的 timeout (沒用)</li><li>乾脆把 azure service bus topics 降級：改用為比較單純的 azure service bus queue (還是有問題)</li><li>…我覺得沒招了，只好一度暴力改成每次都送 50 個一樣的 message 到 Azure，總有一些會成功 …. (<strong>餵公子吃餅</strong> 🍪)</li></ol><h3>然後我發現問題的深一層：</h3><pre>雖然沒看到「收到 message 時會印的 console.log」，但其實 foo() 一直有被觸發(call)到。</pre><pre>也就是說，其實有收到 message。</pre><p>我發現並且證實的方法是，在一進入 foo() 時，就在 file system touch 一個 random 名字的 file，而我發現這些 file 一直狂噴出來 🚀 ，如下圖：</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1018/1*yB_3NLic2_ErnJCEY_A5mg.png" /><figcaption>(我用 shell command `<strong>watch ls</strong>` 來觀測噴出來的檔案。實際上可以瘋狂噴出更多 🚀🚀🚀。)</figcaption></figure><p>所以我開始<em>懷疑</em>：我在 foo() 裡用的 <a href="https://github.com/shelljs/shelljs">shell.js</a> module 是不是用錯了 (process detach, fork, wait child process 這類問題)</p><p>我也<em>懷疑</em>，是不是用錯 javascript 的 async/await 了 (還<a href="https://www.facebook.com/groups/node.js.tw/permalink/3073360889356687/">跑到 Node.js Taiwan 發問</a>，感謝 community 😅)：</p><p><a href="https://www.facebook.com/groups/node.js.tw/permalink/3073360889356687/">Will Wang</a></p><ol><li><em>懷疑</em>：await 會不會讓出去給別的 function 跑之後，那個 function 因某種原因會接不到 stdout？也就是我<em>懷疑</em> foo 變成某種在背景跑但是 foo 內的 console.log 不會印出來，所以會以為沒跑到</li><li><em>懷疑</em>：如果 function bar() 裡有個 while (1), 而當 while 裡在 await 某個 function 時, 有可能會回到 bar() 嗎？也就是跑出 while 了？</li></ol><p>這裡也做了很多嘗試，包括反覆修改 async 邏輯，但還是一直都找不出問題。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/255/0*yf_yGnYDdQgi3AgC.jpg" /><figcaption>還是懷疑人生好了…</figcaption></figure><h3>然後，我發現為什麼了：</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pEZWT1Ni6pd_fuBtTYOWgQ.png" /><figcaption>(黑洞：滿滿滿滿滿滿滿滿滿滿滿滿滿的 nodemon，不只一頁)</figcaption></figure><h3>htop 裡，看到滿滿滿滿的 nodemon，不知道有幾個同時在跑 (而且都沒有接到 stdout) 😱😱😱</h3><p>也就是說：</p><ol><li>送端一送出 message，<strong>收端就有大量在背景偷跑的 </strong><strong>nodemon 會瞬間收走</strong>大部份或是全部的 message</li><li>而且，大量nodemon收到 message 後就會各自開始瘋狂跑 foo()，跑到 CPU, GPU 跟 RAM 都耗盡，可以持續數十分鐘</li></ol><p>所以我開始在想：nodemon 或 Node.js 是不是在背景幫忙多開 process，因為 pm2 有這類行為。找了一些關鍵字，像是 nodejs nodemon pm2 default background process number 這些去排列組合，不過看不太出來問題所在。</p><h3>結果後來找到一個很關鍵的 nodemon issue：</h3><p><a href="https://github.com/remy/nodemon/issues/1025">Nodemon frequently leaves the child process running (detached) · Issue #1025 · remy/nodemon</a></p><p>重點如下：</p><ol><li><strong>多名 </strong><strong>nodemon user 都有發現 child process 被留在背景繼續跑(登愣！)</strong></li><li>直接用 Ctrl + c (送出 SIGINT) 來結束 nodemon 不一定有用，除非在 nodemon.json 或 code 裡用 process.on() 明確處理 SIGINT/SIGHUP/SIGTERM 這些 signal，<strong>不過有人用了沒效果</strong></li><li>有人 (<a href="https://github.com/heisian">heisian</a>) 提出 root cause 是<strong>因為 </strong><strong>nodemon 用了 Node.js 的 </strong><strong>child_process.spawn 來建 child process，而不是用 </strong><strong>child_process.fork，所以 kill 了 parent 不會 kill children</strong>.<br>(<a href="https://github.com/remy/nodemon/blob/af54f6408c716c36c7f359975c9458f8ac348476/lib/monitor/run.js#L85">nodemon uses </a><a href="https://github.com/remy/nodemon/blob/af54f6408c716c36c7f359975c9458f8ac348476/lib/monitor/run.js#L85">child_process.spawn instead of </a><a href="https://github.com/remy/nodemon/blob/af54f6408c716c36c7f359975c9458f8ac348476/lib/monitor/run.js#L85">child_process.fork to create child processes</a>. In this case, killing the parent does not kill its children.)</li><li>nodemon 作者 (<a href="https://github.com/remy">remy</a>) 回應說，因為他需要 spawn 提供的 IPC 功能，而 fork 沒有</li><li>討論到一半，有人就先把他 project 用的 nodemon 幹(換)掉了…XD (改用 babel-watch )</li><li>後來作者還是接受了修改的 PR (可以參考 nodemon 的 commit 62a361c, 在 2017/12/13, 重點 diff 見下圖)</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*064xspHyoyqqvoKWtqakeQ.png" /><figcaption>commit 62a361c: “fix: use fork child node processes”</figcaption></figure><h3>可是瑞凡，<strong>到了 2018 年底了還是不斷有人回報類似</strong>問題，噴到作者說請大家另外開 issue …</h3><p>問題查到這裡，我突然想到一句之前引起討論的話：</p><p><a href="https://github.com/denoland/deno/issues/25">求不要更新了，老子学不动了</a> …..XDDDD (<a href="https://www.zhihu.com/question/279356207">知乎討論</a>)</p><p>回頭是岸，我目前並沒有打算再花時間挖這個問題，如果有人更了解這個問題，歡迎留言指導一下 😅 ( nodemon 的 <a href="https://github.com/remy/nodemon">Github README</a> 有提到一些 signal 處理，也許是線索)</p><h3>到此，我的個人結論：</h3><pre>我不敢用 nodemon 了</pre><p>Note:</p><ol><li>我用的 nodemon 版本是 1.18.10</li><li>我只有把 nodemon 用在 dev 環境</li><li>也有可能根本是我自己使用方法錯誤</li><li>後來因為這個 issue 還有其他原因，我用 python 重寫了這個 service。如果還是有 nodemon 這種需求，建議可以試著確認更深的 root cause。另外，還可以比較看看 pm2, forever, node-supervisor, node-dev, babel-watch。走刻苦手工路線的話也可以用 <a href="https://en.wikipedia.org/wiki/Inotify">Inotify</a> 跟 watchdog (<a href="https://www.systutorials.com/docs/linux/man/8-watchdog/">man</a>) <a href="https://askubuntu.com/questions/251577/how-to-supervise-and-automatically-restart-a-process">(SO1)</a> <a href="https://superuser.com/questions/507576/how-to-automatically-restart-a-linux-background-process-if-it-fails">(SO2)</a> 自己兜 …</li></ol><p>最後，附上一張直接用 node 跑的「乾淨」 htop 圖，供參考：</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/682/1*zA8nAo-vc1pYgMjK8F3uog.png" /><figcaption>(正在跑的 node 跟 node 自己生出來的 thread.)</figcaption></figure><h3>總結一下過程</h3><pre>1. 用了 Azure Pub/Sub(還有 Queue)，本來沒問題，但後來會一直收不到 message<br>2. 發現其實有收到 message，但是印不出 log<br>3. 發現 htop 裡有滿滿滿滿 nodemon 被留在背景跑<br>4. 現在的 nodemon 似乎還是會有這現象<br>5. 我逃了</pre><p>如果有寫錯或是需要更新的，歡迎在留言提醒我~ 😃</p><p><strong><em>非常感謝 </em></strong>幾位好友幫忙 review 了這篇文章 (A-Z)：黃焌華 / <a href="https://medium.com/u/a53248b248a4">Clyde Wu</a> / Fischer Liu / <a href="https://medium.com/u/bf7b5171122f">gasolin</a> / <a href="https://medium.com/u/5f447e49a40f">John Hu</a> / <a href="https://medium.com/u/81cba4ed210f">kcliu</a> / Luke Chang / <a href="https://medium.com/u/9990acc8ce27">Paul Yang</a> / Ricky Chien / <a href="https://medium.com/u/6d9a54455098">Sean Lee</a> / <a href="https://medium.com/u/3a5023952c25">Yu-Jen Chang</a></p><p>一些關於我的資訊：</p><p><a href="https://medium.com/@will.wang/%E9%97%9C%E6%96%BC%E6%88%91-will-wang-ikari-489ba6063dbf">關於我。 (Will Wang / IKARi)</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2651c7f8307e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[撥開 Python, pip, site-packages 的藍色蜘蛛網 ]]></title>
            <link>https://medium.com/@will.wang/%E6%92%A5%E9%96%8B-python-pip-site-packages-%E7%9A%84%E8%97%8D%E8%89%B2%E8%9C%98%E8%9B%9B%E7%B6%B2-90e398bb3785?source=rss-f512818fc477------2</link>
            <guid isPermaLink="false">https://medium.com/p/90e398bb3785</guid>
            <category><![CDATA[virtualenv]]></category>
            <category><![CDATA[pip]]></category>
            <category><![CDATA[ipython]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[pip3]]></category>
            <dc:creator><![CDATA[Will Wang 王興謙]]></dc:creator>
            <pubDate>Sat, 13 Oct 2018 07:13:10 GMT</pubDate>
            <atom:updated>2019-11-26T06:46:35.096Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/492/0*bt59Cgos6aclzvKM.png" /><figcaption>From: <a href="https://xkcd.com/1987/">https://xkcd.com/1987/</a></figcaption></figure><pre>處理過 Python 環境問題的人看到上圖大概會有一些感覺... 😝</pre><h3>Python 環境問題可能是：</h3><ol><li><strong>電腦有超多 Python 版本</strong>，有 python, python2.7, python3, python3.4, python3.6, python9527… 每個版本是從哪裡來的最後也搞不清楚，brew, apt-get, yum, anaconda, pip, easy_install, pyenv, OS 自己帶的，自己抓的執行檔，自己抓 tarball 來 build 的。慘的話，可能同樣版本還有不同份在不同 path 上</li><li><strong>電腦有好幾個 </strong><strong>pip 版本</strong>，有 pip , pip3 , 可能還有 pip3.4, pip3.6, 然後這些 pip 版本自己又有分版本，像是 pip 10.0.1 或 pip 9.0.1 這種。</li><li>不知道 pip 跟 python 的關係是什麼</li><li>不知道 python 在 import 時，dir 尋找順序是什麼</li><li>pip install 有時因為權限問題而提示我可以加個--user ，但不懂加了是裝在哪，不加又是裝在哪</li><li>大概知道 python 的 package 好像是裝在 site-packages 這個資料夾，結果後來發現電腦裡有幾百萬個 site-packages</li><li>Python 升級之後，裝過的 package 又不知道跑到哪裡去了，一直 ImportError: No module named ‘xxx’ (或 ModuleNotFoundError: No module named ‘xxx’ )</li></ol><p>這些問題我常常看到在身邊還有網路上有人問，也都是我以前真實碰過的問題。自己本來是可以用就好，Python 書也不太會把這些解釋得很清楚（書通常是在講 import 邏輯或是 setuptools , distutils 等等）。而最近不想含混過去，就稍微釐清了一下以上問題，然後筆記一下重點，希望讓也想了解的人節省一點時間。</p><h3>先不討論衍生的東西</h3><p>然後，光是以上這些問題就算是一個主題了，所以這裡先不試著去討論後來衍生出的 virtualenv, venv, pyenv, pyvenv, virtualenvwrapper, pipenv, pyenv-virtualenvwrapper …，這些東西都是為了解決一些既有問題而生的 tool，其中有幾個可以說是必備。如果有興趣我覺得<a href="https://stackoverflow.com/a/41573588/1826014">這篇 Stack Overflow 的整理</a>不錯。</p><p>關於 import 邏輯，還有 $PYTHONPATH 這個環境變數，這裡也不贅述了；簡單的話可以直接咕狗，我是建議看書比較系統化跟完整，推薦 精通 Python 3 程式設計 跟 Python 參考手冊(Python Essential Reference, 4/e) 。</p><h3>幾個簡單的大觀念：</h3><blockquote>建議用這樣的順序理解下去 ↓</blockquote><h4>到處都有 site-packages 資料夾 🌏</h4><ol><li>site-packages 是一個資料夾，放我們安裝的 python package，可能<strong>到處</strong>都有</li><li><strong>每安裝一個 python 版本，對於版本 </strong><strong>X.Y 都有各自的 </strong><strong>site-packages</strong>。<br>比方說 python 3.4 跟 python 3.6 都各自有分開的 site-packages 在不同的路徑，而 python 3.6.1 跟 python 3.6.2 的是同一個</li><li><strong>每個 user 可以有自己的 </strong><strong>site-packages</strong> ，在 home 目錄下的某個地方（如果有興趣可以 google site 這個 package 的 site.USER_SITE，或看這個<a href="https://docs.python.org/3.7/install/index.html#alternate-installation-the-user-scheme">官方文件</a>(for python 3.7)）</li><li>ipython 跟一些 tool 裝了之後也有自己的 site-packages</li></ol><h4>pip / pip3 指令在結構上是什麼？ 👭</h4><pre>pip 是個 python script，也是個 python package</pre><ul><li><strong>python script</strong>: 我們執行的 pip，只是一個開頭的 python script，裡面會再去 import 電腦裡「某一個」 site-packages 資料夾裡「真正的」 pip package 來跑。(推薦用 which pip 去看一下 script 內容)</li><li><strong>python package</strong>: 承上，至於會 import 到哪一個 site-packages 下的 pip package，是看我們怎麼執行 pip（稍後細說）</li></ul><h4>pip 版本有兩個層次：</h4><ol><li>分 pip 或 pip3：<br>pip 用來管理 python 2.x 用的 package，而 pip3 用來管理 python 3.x 用的 package。他們各自獨立，pip 升級不會變成 pip3，要裝 pip3 也不需要先有 pip。</li><li>pip (或 pip3) 有自己本身(pip package)的版本：<br>執行 pip --version 可以看到…</li></ol><pre>pip 10.0.1 from /usr/local/lib/python2.7/site-packages/pip (python 2.7)</pre><p>這裡的 10.0.1 就是 pip package 的版本 (也可以看到實際上跑到的 pip package 在哪個資料夾)</p><p>以上 pip3 跟 pip 都是一樣道理，有興趣可以從 which pip3 當作源頭開始追看看 😃</p><h4>那 python import 時會去看哪些 dir？ 📂</h4><p>想知道是從哪些路徑，用什麼順序去找 package，這裡有兩個方法：</p><ul><li>第一個：在 python 裡先 import sys 再看 sys.path 的值</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DB0htOrKmOtYqZq2SKEZvw.png" /></figure><ul><li>第二個：在 shell 執行 python -m site (或 python3 -m site)</li></ul><p>可以觀察一下這兩個方法的輸出，有一些差別。</p><h4>用不同方式執行 pip ，會 import 到不同資料夾的 pip package 😕</h4><p>幾種方式的差別：</p><pre>1. pip<br>2. python /usr/local/bin/pip<br>3. python -m pip</pre><ol><li><strong>直接執行 </strong><strong>pip ：</strong><br>shell 會找到$PATH 裡第一個 pip script，依照這 script 裡的 <a href="https://zh.wikipedia.org/wiki/Shebang">shebang</a> (就是#!/usr/bin/python 這種東西)來決定用哪個版本的 python 跑，所以會用那個版本 python 的 sys.path 去找 pip package 來用。有興趣的人可以試試去改 shebang 的版本，看會發生什麼事。</li><li><strong>用 </strong><strong>python 跑指定路徑的 </strong><strong>pip script：<br></strong>這樣shell 執行的python 版本會壓過 shebang 指定的 python 版本。比方說，這裡執行的 python 如果是 2.7，就算 shebang 雖然指定要用 python3 跑，最後還是會用 2.7 去跑；所以，這樣就會從 python2.7 的 sys.path 裡去找pip package。</li><li><strong>用 </strong><strong>python 的 -m 參數跑 </strong><strong>pip：<br></strong>這樣會去用 shell 執行的 python 版本的 sys.path 裡的pip package(好繞…)。如果 python 指令的版本是 2.6，那就是跑 2.6 裡的 pip package。<br>man page 說明:</li></ol><pre><strong>-m module-name</strong>: Searches sys.path for the named module and runs the corresponding .py file as a script.</pre><p>以上不管是哪一種，如果沒有任何一個 site-packages 裡有 pip，就 import error 跑不起來。</p><h4>跑了 pip install，那東西到底被裝到哪裡去了？ 😕</h4><p>pip install 會有預設安裝的 dir，也會因為各種參數去改變，像是：<br>--target, --user, --prefix, --root 等等。</p><p>有兩個方法可以看被裝在哪：</p><ol><li>最簡單的方法是剛才講到的，pip --version (或是pip -V)會顯示目前這個 pip 是跑哪一個 site-packages 下的 pip package ，這個路徑就是 install 時會放的 dir</li><li>或，比較間接的方法，先用 pip list 列出所有已經安裝的 pkg，然後用 pip show [某pkg] 就會寫 Location 在哪</li></ol><p>如果是用 pip3，方法也是一模一樣。</p><blockquote>↑ 以上這些，我覺得是最重要的 😃</blockquote><h3>其他你可能想知道的：</h3><h4>如何安裝 pip 本身(不是用 pip install 去裝東西)</h4><p>如果電腦裡沒有 pip 或是需要重新安裝，有好幾種安裝 pip 的方法：</p><ol><li>可以透過 OS 裡 apt/yum這種 <a href="https://packaging.python.org/guides/installing-using-linux-tools/">package manager</a></li><li>也可以<a href="https://pip.pypa.io/en/stable/installing/">下載一個 get-pip.py 檔案</a>然後用 python 去執行這個 script。這種就會跟文章前面所敘述的一樣，用不同版本 python 去跑會有差。</li></ol><h4>升級 pip 本身</h4><p>pip 可以用自己來升級自己，像是從9.0.1升級到10.0.1。</p><p>這裡有個很有意思的問題…現在如果要升級 pip3，command 要怎麼下？可能是：</p><ol><li>pip3 install --upgrade pip</li><li>pip install --upgrade pip3</li><li>pip3 instal --upgrade pip3</li><li>pip install --upgrade pip</li></ol><p>猜猜哪個是對的做法？ㄎㄎ</p><h4>dist-packages 跟 site-packages 的差別</h4><p>有時候會看到這兩個詞似乎是可以交換用的，差別在哪？可以看 <a href="https://stackoverflow.com/questions/9387928/whats-the-difference-between-dist-packages-and-site-packages">[這篇]</a>，簡略的說是Debain/Ubuntu 為了降低 conflict 的慣例。</p><p>Python 環境的問題錯綜複雜，如果有寫錯或是需要更新的，歡迎在留言更正我一下噢！ 😃 🙇</p><p>有幾位朋友幫忙 review 了這篇文章，感謝！</p><p>Reviewer (A-Z): <a href="https://medium.com/u/ff1ed511a17a">Askeing Yen</a> / <a href="https://medium.com/u/490d326346d9">Kilik Kuo</a> / <a href="https://medium.com/u/9990acc8ce27">Paul Yang</a> / <a href="https://medium.com/u/97d4c459a99c">samuelololol</a> / <a href="https://medium.com/u/fc3b7a3c21db">Vincent Liu</a> / <a href="https://medium.com/u/21b7b9c957fc">Walter Chen</a></p><p>一些關於我的資訊：</p><p><a href="https://medium.com/@will.wang/%E9%97%9C%E6%96%BC%E6%88%91-will-wang-ikari-489ba6063dbf">關於我。 (Will Wang / IKARi)</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=90e398bb3785" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[關於我。 (Will Wang / IKARi)]]></title>
            <link>https://medium.com/@will.wang/%E9%97%9C%E6%96%BC%E6%88%91-will-wang-ikari-489ba6063dbf?source=rss-f512818fc477------2</link>
            <guid isPermaLink="false">https://medium.com/p/489ba6063dbf</guid>
            <category><![CDATA[programmer]]></category>
            <category><![CDATA[technical-writing]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[firefox]]></category>
            <category><![CDATA[mozilla]]></category>
            <dc:creator><![CDATA[Will Wang 王興謙]]></dc:creator>
            <pubDate>Wed, 10 Oct 2018 06:42:14 GMT</pubDate>
            <atom:updated>2020-08-08T07:02:59.895Z</atom:updated>
            <content:encoded><![CDATA[<h3>關於我。(Will Wang)</h3><pre>「我熱愛關於軟體開發的任何事情，理想是開發出讓社會及全人類過得更好的軟體。」</pre><p>我目前在 Appier 擔任後端工程師，開發 AI 相關的產品，主要關注在 recommendation system。</p><p>2018年，加入 AI 新創公司 <a href="https://www.linkernetworks.com/">Linker Networks</a>，開發 Edge computing 產品與 AI training/inferencing 的後端 infrastructure。</p><p>2015年，由於一直以來的理想，我加入了 <a href="https://www.mozilla.org/en-US/">Mozilla</a> ，主要是在開發 Firefox 瀏覽器的 <a href="https://wiki.mozilla.org/Firefox/session_restore">Session Restore 功能</a>、增強各式各樣的 Performance，還有開發 <a href="https://wiki.mozilla.org/B2G/Bluetooth">Firefox OS 的 Bluetooth Profile</a>。</p><p>更詳細的工作歷程，可以參考我的 <a href="https://www.linkedin.com/in/willwang512/">Linkedin</a>。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*rxFkHCzNL_zomcWSCaPqtA.jpeg" /><figcaption>2017 年於 Mozilla Taiwan 座位前</figcaption></figure><p>另外，我也是兩個可愛的兒子的爸爸 😍</p><p>我喜歡東寫西寫，喜歡分享我的想法跟經驗；之前曾經為有物報告寫了<a href="https://yowureport.com/author/ikari-ikarigmail-com/">一些科技相關的文章</a>；想要多認識我的話，可以考慮看我的 <a href="https://www.facebook.com/sc.wang">Facebook</a>，不過小心廢文很多 😜 另外還有多年沒動的 <a href="http://ikariwing.blogspot.com/">Blogger</a> 跟 <a href="https://www.plurk.com/IKARi">Plurk</a>。</p><p>如果當你看到我寫的文章，覺得有學到東西或是解開了工作上的困擾，不妨考慮請我喝杯咖啡 🍺 ☕️</p><p>有任何想法都歡迎留言哦！ 😃</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=489ba6063dbf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[讓工程師省下時間來寫 code：Evernote 的簡報功能]]></title>
            <link>https://medium.com/@will.wang/%E8%AE%93%E5%B7%A5%E7%A8%8B%E5%B8%AB%E7%9C%81%E4%B8%8B%E6%99%82%E9%96%93%E4%BE%86%E5%AF%AB-code-evernote-%E7%9A%84%E7%B0%A1%E5%A0%B1%E5%8A%9F%E8%83%BD-96720c4b688b?source=rss-f512818fc477------2</link>
            <guid isPermaLink="false">https://medium.com/p/96720c4b688b</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[time-management]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[evernote]]></category>
            <category><![CDATA[presentations]]></category>
            <dc:creator><![CDATA[Will Wang 王興謙]]></dc:creator>
            <pubDate>Sat, 18 Feb 2017 09:43:41 GMT</pubDate>
            <atom:updated>2017-02-18T09:43:41.936Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o7PcNrII5NbI5mwPnbh_5Q.png" /><figcaption>實際使用簡報功能的畫面。</figcaption></figure><p>昨天在部門內部 sharing 的時候，我試用了 Evernote 專業版的簡報功能，並直接輸出 PDF（如下）：</p><p><a href="https://drive.google.com/file/d/0B4Erl45yZS6LS0dHRlhWVS1PYlU/view?usp=sharing">Rust 白話說_Macros_Will_Wang.pdf</a></p><ol><li><strong>簡報功能最大的好處</strong>：如果沒特別需求去調投影片裡細微的圖文 layout，就可以直接把平常的筆記當成投影片投放，<strong>不用另外花費做投影片的時間</strong>（像有些是 copy paste），第二是可以避免像是 code branch 的問題（自己的筆記更新了但投影片是舊的，沒再花時間去改的話，下次如果再報就是過時的）。</li><li><strong>彈性分頁面的概念</strong>：以前如果投影片頁面內的文字一變多，要拆成兩頁，原本要：剪下多餘文字&gt;另開(or複製)頁面&gt;貼上&gt;調整相關頁面…etc，而且很可能偶爾就會出現一下這種事；現在可以直接改變分頁的點；或是有需要的話，直接做成比較大的一頁也行（我附檔的其中一頁就是這樣設計）</li><li><strong>可以輸出簡報為 PDF</strong>：跟簡報模式一樣，滿精緻漂亮的！</li><li><strong>其他比較 minor 的好處</strong>：即時變大投影片的文字跟圖片，即時改就反映到簡報上，用 Evernote 做簡報等同跨平台編輯，直接修改內圖，簡報的字型漂亮，…。</li></ol><p>其實其他像是 markdown 這樣的東西也有轉成投影片的功能，只是我猜測可能沒那麼順手。</p><p>目前發現簡報功能的缺點：沒有太多微調簡報的方法，這就要看該次簡報適不適合；另外好像也沒方式打上頁碼。</p><p>（順帶一提，這次 sharing 主題是 rust 語言，各位 rust 能人異士如果看到投影片裡的錯誤，可以直接留在 comment，小弟就會去修正喔!）</p><p>Note: 這篇文章其實也是為了要測試 Medium 的一些功能，原本貼文在 facebook（所以反而 Medium 這裡的內容比較新）：</p><iframe src="https://cdn.embedly.com/widgets/media.html?type=text%2Fhtml&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;schema=facebook&amp;url=https%3A//www.facebook.com/sc.wang/posts/10154982275078255&amp;image=" width="600" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/616baeacbc0c031f09c6f932b2f95242/href">https://medium.com/media/616baeacbc0c031f09c6f932b2f95242/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=96720c4b688b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why medium?]]></title>
            <link>https://medium.com/@will.wang/why-medium-23d9e2a27f5f?source=rss-f512818fc477------2</link>
            <guid isPermaLink="false">https://medium.com/p/23d9e2a27f5f</guid>
            <category><![CDATA[writing]]></category>
            <category><![CDATA[blogging]]></category>
            <category><![CDATA[travel]]></category>
            <dc:creator><![CDATA[Will Wang 王興謙]]></dc:creator>
            <pubDate>Tue, 17 May 2016 18:43:02 GMT</pubDate>
            <atom:updated>2017-02-18T11:39:32.066Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>“focus is about saying no”</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0TBVrIDtkFhiJXhHEKl6HQ.png" /></figure><p>我到現在依然還沒體會出為何要在 medium 寫文章的原因 (thinking)，不過看到 <a href="https://medium.com/u/4a6ce25fcf6e">Yuren Ju</a> 在這寫了好幾篇好文，像是：</p><p><a href="https://medium.com/p/db7fbbb5cef5">攝影師之眼 — 金邊觀察</a></p><p>感覺還頗棒的呢 ;-)</p><p>於是，這是我的第一篇 medium 文章，只是因為一個 fu 而來。</p><p>隨筆：訂英國的火車票碰到網站 bug，真是讓人痛苦啊，記上一筆。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=23d9e2a27f5f" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>