The achieved the latter by making "the right way" also be "the commonly-used way"
Be Inc did 2 things well in this regard
* They designed their API/App Framework so that launching threads was easy, and passing messages between threads was safe & easy
* They developed a culture (both within the organisation and also within their developer community) of using those features everywhere.
Those two things are more closely linked than they may appear. The framework was explicitly message-based. You interacted with different components by sending and receiving messages. And it was heavily threaded (Be called it "pervasive multi-threading"), to the extent that every window had its own thread, separate to the main app thread.
By having those features, and not having any particularly good way to write apps except by using the official framework, the developer community was forced to learn how to develop multi-threaded, message-passing apps.
And once developers started to think in terms of messages and threads, it was natural for them to use those elements to solve all sorts of problems, so that no self-respecting BeOS developer would ever think to do significant processing inside any of the UI handling threads.
Garbage collection was left to the application, the way it is on iOS before v5 (if I'm not mistaken), C, C++ and many other languages. While it does increase mental load on the developer (compared to gc languages e.g. Lisp, Java, C# or Python), it is not too hard. It's still the norm in C++, for example.
And like node.js, they tried to avoid blocking APIs (and mostly succeeded), but provided a very simple way to "outsource" them to other threads. You can do that in C or Java as well, but it's not as easy or as pervasive as it is in BeOS.
* APIs blocking on network operations (e.g., gethostbyname())
How does BeOS avoid/avoid lagging with these things?