社運訊息網絡與網路自由

Facebook: Page not found

所有自由議題都是相連的,他們在發展的過程互相影響,直到今日,在實際的運作面,他們也會互相碰觸,相輔相成。

陳為廷分享這篇新聞《張志軍來台 網路也戒嚴?在 Facebook 上說

這真的不尋常。昨天本來想看一下中強律師的評論,搜尋發現找不到。

才想到,早上就聽他說在房內被破門前,臉書就莫名被停權了。

加上難攻、和李茂生、沃草先後被限制權限。這絕對不是單一事件。

新聞和評論的臆測成分都很重,非常有可能這些帳號都只是被惡意檢舉才被限制發文的,而不是政府與 Facebook 有何私下的協議。但這個現象又再度驗證了,我們逝去的,去中心化、透明的網路有多麼的重要。有沒有人和他們說他們真正需要的是海盜灣等級的架站、技術,甚至是防禦能力,永不從網路上下架,除非政府從 ISP 端建立防火牆?有沒有人跟他們說,「Facebook 社運」模式(源自於茉莉花革命的 Twitter 社運模式)在動員和擴散上雖然有效,這些工具也是中心化架構的致命弱點?有沒有人帶著上一代網路人的慚愧,和他們說,這是我們所想像的網路,但對不起,因為我們沒有做到,所以你的訊息才會被這樣被封殺?

我一直在私下的管道在 Mozilla 內部說,Open Web 作為組織的任務,勢必代表立場必須隨著時代更新,同意更多的概念是確保網路自由的基石:網路中立性、去中心化、公開規格硬體(Open source hardware)、反言論審查……。立場也不該隨著國家與地區,為了方便而扭曲。這些訊息都是可以在我們在產品上妥協的同時發展,而不是縮限觀點來自圓其說現在的產品。

如果不做這些事情,做低階手機讓接下來的十億人能夠更容易上網,最後到底可以幹嘛?

Use Promise, and what to watch out if you don’t

If you do know Promise, consider the following code; do you know the order of the resulting log? (answered below)

var p = new Promise(function(resolve, reject) {
  console.log(1);
  resolve();
})
p.then(function() {
  console.log(2);
});
console.log(3);
setTimeout(function() {
  console.log(4);
});
p.then(function() {
  console.log(5);
});
console.log(6);
setTimeout(function() {
  console.log(7);
});
console.log(8);

The Promise interface

The Promise interface is one of the few generic interfaces that graduated from being a JavaScript library to be a Web platform API (The other being the JSON interface.) You already know about it if you have heard of Q, or Future, or jQuery.Deferred. They are similar, if not identical, things under different names.

Promise offers better asynchronous control to JavaScript code. It offers a chain-able interface, where you could chain your failure and success callbacks when the Promise instance is “rejected” or “resolved”. Any asynchronous call can be easily wrapped into a Promise instance by calling the actual interface inside the synchronous callback function passed when constructing the instance.

The ability to chain the calls might not be a reason appeal enough for the switch; what I find indispensable is the ability of Promise.all(); it manages all the Promise instances on your behalf and “resolves” the returned promise when all passed instances are resolved. It’s great if you want to run multiple asynchronous action in parallel (loading files, querying databases) and do your things only if everything have returned. (The other utility being Promise.race(), however I’ve not found a use case for myself yet.)

Keep in mind there is one caveat: compare to EventTarget callbacks (i.e. event handlers), this in all Promise callbacks are always window. You should wrap your own function in bind() for specific context.

The not-so-great alternatives

Before the Promise interface assume it’s throne in the Kingdom of Asynchronous Control, there are a few alternatives.

One being the DOMRequest interface. It feels “webby” because it’s inherited from the infamous EventTarget interface. If you have ever add a event listener to a HTML element, you have already worked with EventTarget. A lot of JavaScript developers (or jQuery developers) don’t work with EventTarget interface directly because they use jQuery, which absorb the verboseness of the interface (and difference between browser implementations). DOMRequest, being an asynchronous control interface simply dispatches success and error events, is inherently verbose, thus, unpopular. For example, you may find yourself fighting with DOMRequest interface if you want to do things with IndexedDB.

Another terrible issue with DOMRequest is that it’s usage is entirely reserved for native code, i.e. you can not new DOMRequest() and return the instance for the method of your JavaScript library. (likewise, your JavaScript function cannot inherit EventTarget either, which is the reason people turned to EventEmitter, or hopelessly dispatch custom event on the window object. That also means to mock the APIs inheriting EventTarget and/or returning DOMRequests, you must mock them too.)

Unfortunately, given the B2G project (Firefox OS) was launched back in 2011, many of the Web API methods return DOMRequest, and new methods of these APIs will continue to return DOMRequest for consistency.

The other alternative would be rolling your own implementation of generic asynchronous code. In the Gaia codebase (the front-end system UIs and preload web apps for B2G), there are tons of example because just like many other places in Mozilla, we are infected with Not-Invented-Here syndrome. The practices shoot us in the foot because what thought to be easily done is actually hard to done right. For example, supposedly you have the following function:

function loadSomething(id, callback) {
    if (isThere(id)) {
      getSomething(id, callback);

      return;
    }

    var xhr = new XMLHttpRequest();
    ...
    xhr.onloadend = function() {
      registerSomething(id, xhr.response);
      callback(xhr.response);
    };
    ...
}

To the naïve eyes there is nothing wrong with it, but if you look closely enough you will realize this function does not return the callback asynchronously every time. If I want to use it:

loadSomething(id, function(data) {
  console.log(1, data);
}); 
console.log(2);

The timing of 1 is non-deterministic; it might return before 2, or after. This creates Schrödinger bugs and races that will be hard to reproduce, and fix.

You might think a simple solution to the problem above would be simply wrap the third line in setTimeout(). This did solve the problem but it comes with issues of its own, not to mention it further contribute to the complexity of the code. Wrap the entire function, instead, in a Promise instance, guarantees the callbacks runs asynchronously even if you have the data cached.

(Keep in mind that the example above have lots of detail stripped; good luck finding the same pattern when someone else hides it in a 500-line function between 10 callbacks.)

Not-Invented-Here syndrome also contribute to other issues, like every other software project; more code means more bugs, and longer overhead for other engineers to pick up.

Conclusion

In the B2G project, we want to figure out what’s needed for the Web to be considered a trustworthy application platform. The focus has been enabling hardware access for web applications (however sadly many of the APIs was then restricted to packaged apps because of their proprietary nature and security model), yet I think we should be putting more focus on advancing common JavaScript interfaces like Promise. I can’t say for sure that every innovation nowadays are valid solutions to the problems. However, as the saying goes, the first step toward fixing a problem is to admit there is one. Without advances in this area, browser as an application runtime will be left as-is, fill with legacies for its document reader era and force developers to load common libraries to shim it. It would be “a Web with kilobytes of jquery.js overhead.”, one smart man once told me.

(That’s one reason I kept mention EventTarget v.s. EventEmitter in this post: contrary to Promise v.s. DOMRequest, the EventEmitter use case have not yet been fulfilled by the platform implementations.)


The answer to the question at the beginning is: 1, 3, 6, 8, 2, 5, 4, 7. Since all the callbacks are asynchronous except (1), only (1) happens before (3), (6), and (8). Promise callbacks (2) and (5) are run asynchronous and they return before setTimeouts.

當代電腦:簡短的歷史故事與更短的抱怨

此文經由作者同意,翻譯自 David KendalModern computing: A short history and a shorter rant


在最初,使用電腦很不方便。要使用電腦就一定要學會寫程式:除此之外,沒有其他使用電腦方式了。學寫程式很難,它跟學習一種新的符號語言有關,而該符號語言只是設計用來給予電腦指令。

(此文將「當代」定義起始於微電腦時代(約 1970 年)以符合文意,但前述的歷史除了微電腦,也可以套用在與更早的大型主機上。)

有不少人,花了很長的時間,讓電腦能夠更容易使用,例如麥金塔(Macintosh)個人電腦,但使用上的方便並沒有使撰寫程式更容易(反而更難,因為撰寫圖形介面有更多新的程式語言寫法,所以程式比起原本在命令列上執行的更大)。因此,雖然有更多人買了這些「容易使用」的電腦,很少人學會寫程式。

到最後,這些設計電腦的人覺得,既然沒有人學寫程式,寫程式應該跟大部分的人無關。沒有人應該要為了用電腦而學寫程式,只有想要成為軟體開發者的人才需要寫程式。他們認為寫程式永遠都會是很難的事情,沒有人會想要學,除了專業、全職的程式設計師。

所以他們開始推出比起前代更「容易使用」的電腦。更加的「容易使用」,容易到人們完全無法在上面執行任何自己的程式。不只是那些讓使用者能撰寫程式的功能消失了,他們反過來不讓你在自己的電腦上執行自己的程式,除非你付額外的費用來啟用「開發功能」。

但是當最早的「容易使用」的電腦出現時,有其他人還是在努力的要讓撰寫程式能更容易,讓人人都有能力寫程式的。畢竟,寫程式很簡單:只要了解執行條件和重複而已。而且到最後他們還找出了連這些都不用了解的寫程式方法。

這些人知道撰寫程式並不是本質上困難的事情,會這樣只是因為工藝尚未精純,且早期的工具很粗糙且簡陋。他們知道他們可以做出更好的寫程式工具。

他們也知道會寫程式是一件有力量與啟發性的事,因為它讓你能夠完全掌握且控制你周遭的電腦裝置(而不是讓別人,像是某個「程式設計師」,幫你決定做他認為你會想要的)。他們知道,因為他們總是時時撰寫自己的程式。他們能讓電腦幫忙做所有想做的事,從幫助自閉兒童選擇最好的照片、或是選擇約會對象。他們擁有改造自己與周遭人們生活的能力,且他們想要將這個能力傳遞給所有人。


當然,這整篇「歷史」應該是現在進行式。不過有些「最近」的歷史的確是過去式:人們已經花了快半個世紀研究如何讓撰寫程式更加容易,只是他們的成果在個人電腦起飛之後多半被忽略。

反之,撰寫程式在新的電腦上變得越變越困難,尤其是 Apple 的裝置。這很可惜,因為 Apple 的設計很好,但他們似乎認為容許可撰寫程式的功能,和好設計與易用性是相衝突的。這樣的易用性根本就是個幻覺,因為在畫面之下,電腦是一模一樣的。更精確的說,這種「易用性」高的電腦是更不好用的:它無法為人人所用,只能提供某些人(專業程式設計師)所預期的行為。

只要「撰寫程式本來就是困難的事」這樣的迷思存在一日,電腦等裝置的真實力量永遠只能被少數人所掌握,且讓撰寫程式更為簡單的工具永遠不會出現。


譯註:

  • Computer、computing:譯為電腦,但泛指所有能夠執行程式的計算裝置(computing device)。可以很拗口的翻譯為「計算機」但是這不是一般詞彙。
  • Programming、programmability:譯為「撰寫程式」,因為沒有常用的中文詞彙。在簡體中文有很精準的動詞,即「编程」。