Skip to content

Commit

Permalink
feat: async/await Example (eggjs#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
popomore authored and dead-horse committed Feb 15, 2017
1 parent 0087214 commit c59eef1
Show file tree
Hide file tree
Showing 19 changed files with 626 additions and 0 deletions.
27 changes: 27 additions & 0 deletions hackernews-async/.autod.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'ues strict';

module.exports = {
write: true,
plugin: 'autod-egg',
prefix: '^',
devprefix: '^',
registry: 'https://r.cnpmjs.org',
exclude: [
'test/fixtures',
],
dep: [
'egg',
],
devdep: [
'autod',
'autod-egg',
'eslint',
'eslint-config-egg',
'egg-bin',
],
keep: [
],
semver: [
],
test: 'scripts',
};
5 changes: 5 additions & 0 deletions hackernews-async/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"parserOptions": {
"ecmaVersion": 2017
}
}
37 changes: 37 additions & 0 deletions hackernews-async/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# egg-example-hackernews-async

[Hacker News](https://news.ycombinator.com/) showcase using async/await for egg

## QuickStart

### Development
```shell
$ npm install
$ npm run dev
$ open http://localhost:7001/
```

### Deploy

Use `EGG_SERVER_ENV=prod` to enable prod mode

```shell
$ EGG_SERVER_ENV=prod npm start
```

### Npm Scripts

- Use `npm run autod` to auto detect dependencies upgrade
- Use `npm run lint` to check code style
- Use `npm test` to run unit test

### Requirement

Please ensure your node version is `>=7.6.0` for async await support without flag. If your node version is `>=7.0.0 < 7.6.0`, you can run npm scripts with harmony flag

```shell
# start server
npm run dev -- --harmony-async-await
# run test cases
npm run test-local -- --harmony-async-await
```
33 changes: 33 additions & 0 deletions hackernews-async/app/controller/news.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

module.exports = app => {
return class NewsController extends app.Controller {
async list() {
const { ctx, app } = this;
const pageSize = app.config.news.pageSize;
const page = parseInt(ctx.query.page) || 1;

const idList = await ctx.service.hackerNews.getTopStories(page);

// get itemInfo parallel
const newsList = await Promise.all(idList.map(id => ctx.service.hackerNews.getItem(id)));
await ctx.render('news/list.tpl', { list: newsList, page, pageSize });
}

async detail() {
const { ctx } = this;
const id = ctx.params.id;
const newsInfo = await ctx.service.hackerNews.getItem(id);
// get comment parallel
const commentList = await Promise.all(newsInfo.kids.map(id => ctx.service.hackerNews.getItem(id)));
await ctx.render('news/detail.tpl', { item: newsInfo, comments: commentList });
}

async user() {
const { ctx } = this;
const id = ctx.params.id;
const userInfo = await ctx.service.hackerNews.getUser(id);
await ctx.render('news/user.tpl', { user: userInfo });
}
};
};
7 changes: 7 additions & 0 deletions hackernews-async/app/extend/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const moment = require('moment');

exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();

exports.domain = url => url && url.split('/')[2];
203 changes: 203 additions & 0 deletions hackernews-async/app/public/css/news.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
body,
html {
font-family: Verdana;
font-size: 13px;
height: 100%
}
ul {
list-style-type: none;
padding: 0;
margin: 0
}
a {
color: #000;
cursor: pointer;
text-decoration: none
}
#wrapper {
background-color: #f6f6ef;
width: 85%;
min-height: 80px;
margin: 0 auto
}
#header,
#wrapper {
position: relative
}
#header {
background-color: #f60;
height: 24px
}
#header h1 {
font-weight: 700;
font-size: 13px;
display: inline-block;
vertical-align: middle;
margin: 0
}
#header .source {
color: #fff;
font-size: 11px;
position: absolute;
top: 4px;
right: 4px
}
#header .source a {
color: #fff
}
#header .source a:hover {
text-decoration: underline
}
#yc {
border: 1px solid #fff;
margin: 2px;
display: inline-block
}
#yc,
#yc img {
vertical-align: middle
}
.view {
position: absolute;
background-color: #f6f6ef;
width: 100%;
-webkit-transition: opacity .2s ease;
transition: opacity .2s ease;
box-sizing: border-box;
padding: 8px 20px
}
.view.v-enter,
.view.v-leave {
opacity: 0
}
@media screen and (max-width: 700px) {
body,
html {
margin: 0
}
#wrapper {
width: 100%
}
}
.news-view {
padding-left: 5px;
padding-right: 15px
}
.news-view.loading:before {
content: "Loading...";
position: absolute;
top: 16px;
left: 20px
}
.news-view .nav {
padding: 10px 10px 10px 40px;
margin-top: 10px;
border-top: 2px solid #f60
}
.news-view .nav a {
margin-right: 10px
}
.news-view .nav a:hover {
text-decoration: underline
}
.item {
padding: 2px 0 2px 40px;
position: relative;
-webkit-transition: background-color .2s ease;
transition: background-color .2s ease
}
.item p {
margin: 2px 0
}
.item .index,
.item .title:visited {
color: #828282
}
.item .index {
position: absolute;
width: 30px;
text-align: right;
left: 0;
top: 4px
}
.item .domain,
.item .subtext {
font-size: 11px;
color: #828282
}
.item .domain a,
.item .subtext a {
color: #828282
}
.item .subtext a:hover {
text-decoration: underline
}
.item-view .item {
padding-left: 0;
margin-bottom: 30px
}
.item-view .item .index {
display: none
}
.item-view .poll-options {
margin-left: 30px;
margin-bottom: 40px
}
.item-view .poll-options li {
margin: 12px 0
}
.item-view .poll-options p {
margin: 8px 0
}
.item-view .poll-options .subtext {
color: #828282;
font-size: 11px
}
.item-view .itemtext {
color: #828282;
margin-top: 0;
margin-bottom: 30px
}
.item-view .itemtext p {
margin: 10px 0
}
.comhead {
font-size: 11px;
margin-bottom: 8px
}
.comhead,
.comhead a {
color: #828282
}
.comhead a:hover {
text-decoration: underline
}
.comhead .toggle {
margin-right: 4px
}
.comment-content {
margin: 0 0 16px 24px;
word-wrap: break-word
}
.comment-content code {
white-space: pre-wrap
}
.child-comments {
margin: 8px 0 8px 22px
}
.user-view {
color: #828282
}
.user-view li {
margin: 5px 0
}
.user-view .label {
display: inline-block;
min-width: 60px
}
.user-view .about {
margin-top: 1em
}
.user-view .links a {
text-decoration: underline
}
Binary file added hackernews-async/app/public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions hackernews-async/app/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

module.exports = app => {
app.redirect('/', '/news');
app.get('/news', 'news.list');
app.get('/news/item/:id', 'news.detail');
app.get('/news/user/:id', 'news.user');
};
70 changes: 70 additions & 0 deletions hackernews-async/app/service/HackerNews.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';

module.exports = app => {
/**
* HackerNews Api Service
*/
class HackerNews extends app.Service {
constructor(ctx) {
super(ctx);
this.config = this.ctx.app.config.news;
this.serverUrl = this.config.serverUrl;
this.pageSize = this.config.pageSize;
}

/**
* request hacker-news api
* @param {String} api - Api name
* @param {Object} [opts] - urllib options
* @return {Promise} response.data
*/
async request(api, opts) {
const options = Object.assign({
dataType: 'json',
}, opts);

const result = await this.ctx.curl(`${this.serverUrl}/${api}`, options);
return result.data;
}

/**
* get top story ids
* @param {Number} [page] - page number, 1-base
* @param {Number} [pageSize] - page count
* @return {Promise} id list
*/
async getTopStories(page, pageSize) {
page = page || 1;
pageSize = pageSize || this.pageSize;

const result = await this.request('topstories.json', {
data: {
orderBy: '"$key"',
startAt: `"${pageSize * (page - 1)}"`,
endAt: `"${pageSize * page - 1}"`,
},
});
return Object.keys(result).map(key => result[key]);
}

/**
* query item
* @param {Number} id - itemId
* @return {Promise} item info
*/
async getItem(id) {
return await this.request(`item/${id}.json`);
}

/**
* get user info
* @param {Number} id - userId
* @return {Promise} user info
*/
async getUser(id) {
return await this.request(`user/${id}.json`);
}
}

return HackerNews;
};
Loading

0 comments on commit c59eef1

Please sign in to comment.