Skip to content

Instantly share code, notes, and snippets.

@indongyoo
Last active February 23, 2018 09:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indongyoo/8b821df696b68e23085d93f77fff5a4b to your computer and use it in GitHub Desktop.
Save indongyoo/8b821df696b68e23085d93f77fff5a4b to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="pre.fn.js"></script>
<script src="images.js"></script>
<style>
.container {
border: 1px solid black;
}
body > .alert {
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 1000;
}
body > .alert .body {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 300px;
height: 150px;
background: #fff;
text-align: center;
border-radius: 20px;
}
body > .alert .msg {
padding: 35px 20px 0;
height: 60px;
vertical-align: middle;
}
body > .alert button {
background: #4d90fe;
border: 0 none;
border-radius: 10px;
padding: 8px 16px;
color: #fff;
font-size: 13px;
}
body > .alert button.cancel {
background: #eee;
color: #333;
}
</style>
</head>
<body>
### 0. 서광열님 블로그 중 ...
프로그래밍의 본질은
1) 한 번에 풀 수 없는 크고 복잡한 문제를 작은 문제들로 나누어서 해결하고
2) 그렇게 나온 결과물들을 조합하여 다른 문제를 해결하는 것을 말합니다.
여기서 좋은 프로그램의 가장 중요한 특성으로 조합성(composability)이 등장합니다.
우리는 계속해서 크고 복잡한 문제를 풀어야 하고, 또한 비슷하지만 조금은 다른 문제들을
풀어야 합니다. 앞서 만들어 놓은 산출물을 쉽게 조합하여 새로운 문제를 해결할 수 있다면
프로그래머의 생산성은 비약적으로 늘 수 있기 때문입니다.
### 1. 비동기 상황을 보다 간결하게 표현하기 위해
<script>
const add10 = a => Promise.resolve(a + 10);
</script>
<script>
const p1 = Promise.resolve(10);
const p2 = Promise.resolve(10);
// Vanilla
p1.then(add10).then(add10).then(add10).then(console.log);
// Functional
go(p2, add10, add10, add10, add10, console.log);
</script>
<script>
// Vanilla
const f1 = p => p.then(add10).then(add10).then(add10).then(console.log);
// Functional
const f2 = pipe(add10, add10, add10, console.log);
f1(Promise.resolve(10));
f2(Promise.resolve(10));
</script>
<div id="el1"></div>
<div id="el2"></div>
<script>
function loadImage(src) {
return new Promise(function(resolve) {
var image = new Image();
image.onload = function() { resolve(image); };
image.src = src;
});
}
const infos = [
"https://1906714720.rsc.cdn77.org/http2/tiles_final/tile_0.png",
"https://1906714720.rsc.cdn77.org/http2/tiles_final/tile_1.png",
"https://1906714720.rsc.cdn77.org/http2/tiles_final/tile_2.png"
];
const el1 = document.querySelector('#el1');
const el2 = document.querySelector('#el2');
// Vanilla
Promise
.all(infos.map(loadImage))
.then(imgs => imgs.forEach(img => el1.appendChild(img)));
// Functional
go(infos,
cmap(loadImage),
each(img => el2.appendChild(img)));
</script>
### 2. 콜백 지옥(들여쓰기 문제)을 해결하기 위해서만이 아닌,
복잡한 로직을 가진 비동기 상황을 간결하게 해결하기 위해
<script>
const team1 = {
name: 'italy',
skaters: [
{ time: 600, und: 40 },
{ time: 300, und: 30 },
{ time: 200, und: 20 }
]
};
const team2 = {
name: 'korea',
skaters: [
{ time: 700, und: 30 },
{ time: 300, und: 20 },
{ time: 100, und: 20 }
]
};
function upAndDown(skater) {
return Math.floor((Math.random() * skater.und));
}
function skate(skater) {
return new Promise(function(resolve) {
setTimeout(resolve, skater.time + upAndDown(skater));
});
}
</script>
<script>
// Vanilla
function quarterfinal(teams) {
return Promise
.all(
teams.map(team =>
function skateAll(skaters, i = 0) {
if (skaters.length == i) return;
return skate(skaters[i]).then(_=> skateAll(skaters, i+1));
} (team.skaters)
.then(_=> console.log(`${team.name}!`))
)
)
.then(_=> console.log('준준결승 종료'));
}
// quarterfinal([team1, team2]);
</script>
<script>
// Vanilla 2
function skateAll(skaters, i = 0) {
if (skaters.length == i) return;
return skate(skaters[i]).then(_=> skateAll(skaters, i+1));
}
function start(team) {
return skateAll(team.skaters)
.then(_=> console.log(`${team.name}!`));
}
function semifinal(teams) {
return Promise
.all(teams.map(start))
.then(_=> console.log('준결승 종료!'));
}
// semifinal([team1, team2]);
</script>
<script>
// Functional
const final = pipe(
cmap(team => go(
team.skaters,
each(skate),
_=> `${team.name}!`,
console.log)),
_=> console.log('결승 종료!'));
// final([team1, team2]);
</script>
<script>
each(f => f([team1, team2]), [
quarterfinal,
semifinal,
final]);
</script>
### 3. async/await는 은총알인가?
<script>
function add100(a) {
return Promise.resolve(a + 100);
}
// 해도 안돼요.
// const list = [1, 2, 3, 4];
// const result = list.map(async function(val) {
// return await add100(val);
// });
// console.log(result);
(async function() {
const list = [1, 2, 3, 4];
const result = await map(async function(val) {
return await add100(val);
}, list);
console.log(result);
}) ();
// Functional
go([1, 2, 3, 4], map(add100), console.log);
</script>
### 4. 디자인이 들어간 alert, confirm
<script>
// Simple JQuery
let ui = {
el(html) {
var els, tmp;
if (/^<(tr|th|td).*><\/(tr|th|td)>$/.test(html)) {
tmp = document.createElement('table');
tmp.innerHTML = html;
tmp = tmp.firstChild;
if (RegExp.$1 != 'tr') tmp = tmp.firstChild;
} else {
tmp = document.createElement('div');
tmp.innerHTML = html;
}
return (els = map(v => v, tmp.children)).length == 1 ? els[0] : els;
},
append: curry2(function(parent, child) {
return parent.appendChild(child), parent;
}),
remove: function(el) {
el.parentNode.removeChild(el);
each(arg => el.removeEventListener(...arg), el._events);
return el;
},
on(el, event, sel, f) {
if (arguments.length == 3) return el => ui.on(el, ...arguments);
function handler(e) {
some(c => c == e.target, el.querySelectorAll(sel)) && f(Object.assign(e, { delegateTarget: el }));
}
el.addEventListener(event, handler);
el._events = el._events || [];
el._events.push([event, handler]);
return el;
}
};
!function() {
const tmpl = curry2((cancel, msg) => `
<div class="alert">
<div class="body">
<div class="msg">${msg}</div>
<div class="buttons">
${cancel ? '<button type="button" class="cancel">취소</button>' : ''}
<button type="button" class="ok">확인</button>
</div>
</div>
</div>
`);
const base = curry2(function(tmpl, msg) {
return new Promise(function(resolve) {
go(msg,
tmpl,
ui.el,
ui.on('click', '.ok', function(e) {
ui.remove(e.delegateTarget);
resolve(true);
}),
ui.on('click', '.cancel', function(e) {
ui.remove(e.delegateTarget);
resolve(false);
}),
ui.append(document.querySelector('body')));
});
});
ui.alert = base(tmpl());
ui.confirm = base(tmpl(true));
} ();
var count = 0;
function httpPost(url) {
return new Promise(function(resolve) {
console.log(url, 'post!');
resolve(++count);
})
}
</script>
<script>
// Vanilla
function confirmTest1() {
ui.confirm('바로 결제하시겠습니까?')
.then(function(bool) {
if (bool) {
console.log('결제하러 가기');
} else {
return ui.confirm('그럼 장바구니에 담으시겠습니까?')
.then(function(bool) {
if (bool) {
return httpPost('https://marpple.com/api/add_cart')
.then(count => console.log(`장바구니 아이콘 숫자 올리기 ${count}`))
.then(_=> ui.alert('고맙습니다.'));
}
return ui.alert('이런! 아쉽군요 ㅠㅠ');
});
}
})
.then(_=> console.log('confirmTest1 종료!'));
}
confirmTest1(); // 셋 중에 하나씩 실행해보세요. (아래 confirmTest2, confirmTest3)
</script>
<script>
// Vanilla async/await
async function confirmTest2() {
if (await ui.confirm('바로 결제하시겠습니까?')) {
console.log('결제하러 가기');
} else if (await ui.confirm('그럼 장바구니에 담으시겠습니까?')) {
const count = await httpPost('https://marpple.com/api/add_cart');
console.log(`장바구니 아이콘 숫자 올리기 ${count}`);
await ui.alert('고맙습니다.');
} else {
await ui.alert('이런! 아쉽군요 ㅠㅠ');
}
console.log('confirmTest2 종료!');
}
// confirmTest2();
// 나름 간결하고 깔끔하지만 if + else + statements 는 명령형의 위험성이 숨어 있음
// 필요할 때 async await를 사용하는 것 자체는 동의하지만,
// if, else, i++ j++를 조합해서 사용할 경우 명령형의 위험성을 가지고 있음
</script>
<script>
// Functional
const confirmTest3 = pipe(
cond
(_=> ui.confirm('바로 결제하시겠습니까?'))
(_=> console.log('결제하러 가기'))
(_=> ui.confirm('그럼 장바구니에 담으시겠습니까?'))
(_=> httpPost('https://marpple.com/api/add_cart'),
count => console.log(`장바구니 아이콘 숫자 올리기 ${count}`),
_=> ui.alert('고맙습니다.'))
.else
(_=> ui.alert('이런! 아쉽군요 ㅠㅠ')),
_=> console.log('confirmTest3 종료!'));
// confirmTest3();
// 위와 같은 방식은 IE에서도 가능. (화살표 함수만 사용하지 않으면.)
// cond는 함수에 코드가 갇혀 있고, 인자와 리턴값을 가지고 있어 if, else와는 다름
// cond의 코드 사이에서 map, filter, reduce, find 등을 사용하는 프로그래밍을 할 경우
// 더욱 두 번째 async/await와 차별성이 드러남.
</script>
<script>
var condf =
cond
(1) (_=> console.log('1 이네요'))
(2) (_=> console.log('2 이네요'))
(3) (_=> console.log('3 이네요'))
.else (_=> console.log('else 이네요'));
condf(1);
condf(3);
condf(2);
condf(10);
</script>
### 5. 조금 다른 문제를 간단하게 풀기 - HTTP2로 더 중요해진 동시성
<div class="container">
<canvas id="canvas1"
width="700"
height="500"
style="opacity: 0; transition: opacity 1s"></canvas>
</div>
<script>
// 하나씩 요청해서 모두 가져오면 다 그리고 애니메이션
function renderCanvas1() {
var context = document.querySelector('#canvas1').getContext('2d');
return go(imageDatas,
map(function({ x, y, url }) {
return go(url, loadImage, image => ({ image, x, y }));
}),
each(function({ image, x, y }) {
context.drawImage(image, x, y);
}),
function() {
document.querySelector('#canvas1').style.opacity = 1;
});
}
</script>
<div class="container">
<canvas id="canvas2"
width="700"
height="500"
style="opacity: 0; transition: opacity 1s"></canvas>
</div>
<script>
// 한 번에 여러개 요청해서 모두 가져오면 다 그리고 애니메이션
function renderCanvas2() {
var context = document.querySelector('#canvas2').getContext('2d');
return go(imageDatas,
cmap(function({ x, y, url }) {
return go(url, loadImage, image => ({ image, x, y }));
}),
each(function({ image, x, y }) {
context.drawImage(image, x, y);
}),
function() {
document.querySelector('#canvas2').style.opacity = 1;
});
}
</script>
<div class="container">
<canvas id="canvas3"
width="700"
height="500"></canvas>
</div>
<script>
// 하나씩 요청해서 하나씩 그리기
function renderCanvas3() {
var context = document.querySelector('#canvas3').getContext('2d');
return go(imageDatas,
each(function({ x, y, url }) {
return go(url, loadImage, image => context.drawImage(image, x, y));
}));
}
</script>
<script>
go(void 0, renderCanvas1, renderCanvas2, renderCanvas3)
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment