Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: async/await Example #12

Merged
merged 5 commits into from
Feb 15, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
26 changes: 26 additions & 0 deletions hackernews-async/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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
29 changes: 29 additions & 0 deletions hackernews-async/app/controller/news.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

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

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

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

async user() {
const id = this.params.id;
const userInfo = await this.service.hackerNews.getUser(id);
await this.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