Promise
즉시 return 되고 결괏값을 나중에 쓸 수 있다.
function cal(callback,a,b) {
return callback(a,b)
}
cal((x,y) => x+y , 1,2)
3
비동기 + 콜백 이다. 콜백은 항상 비동기가 아니다.
const callback = () => console.log(1)
setTimeout(callback, 1000);
const p = new Promise((resolve,reject) => {
resolve(1)
})
p.then((result) => {
console.log(result)
})
setimeout처럼 결속되어있지 않고 나중에 결과 값을 쓸 수 있다.
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
Promise.all 보다는 Promise.allSettled 를 써야한다. all의 경우 여러 promise들 중 하나라도 실패(reject)되면 다 실패한다.
allSettled 는 result 들중 실패 성공을 다 나열해준다. all 의 경우 다 catch로 가는데 몇번째 promise가 틀렸는지 알길이 없다.
비동기는 동시의 문제가 아니라 순서의 문제
setTimeout(() => {
console.log(1)
},0)
setTimeout(() => {
console.log(2)
},0)
setTimeout이 callstack 에 올라가자마 즉시 return 이되고 webapi setTimeout에게 위임된다.
webAPI 부분에서는 여러 workthreads 들이 존재하여 병렬로 해결할 수 있다. 다 되면 Task Queue에 결과값을 쌓는다.
event loop 주기마다 call stack에 anonymous 까지 다 끝났으면 Task Queue에서 하나씩 올라간다. () => console.log(1) 이것이 올라가서 실행됨
event loop 는 microtask queue 를 항상 먼저 살펴본다. 그래서 Promise 가 더 빠르게 call stack에 올라간다. 여기가 계속 안비면 TaskQueue(macro queue) 는 실행이 계속 지연됨
let a = 2;
const p = new Promise((resolve,reject) => {
console.log('제일 먼저')
setTimeout(() => {
a = 5;
console.log(a)
resolve(a)
},0)
})
console.log("딴짓")
p.then((result) => {
console.log('result', result)
})
제일 먼저
딴짓
5
result 5
Promise의 동기부분이 존재한다. (resolve,reject) => {} 안의 부분은 다 동기이다.
Promise 가 주어진 익명함수를 호출한다.
promise -> async
async 는 오른쪽에서 왼쪽 , Promise는 왼쪽에서 오른쪽
async function a() {
const a = await 1;
console.log('a',a)
console.log('hmm')
await null
const b = await Promise.resolve(1)
console.log('b',b)
return b
}
Promise.resolve(1)
.then((a) => {
console.log(a)
console.log('hmm')
return null
})
.then(() => {
return Promise.resolve(1)
})
.then((b) => {
console.log('b',b)
return b;
})
하지만 promise ,async 는 1대1 대응이 아니다.
3번째 then에서는 1번쨰 then 인자로 들어온 a를 참조할 수 없다. 계속해서 then 의 인자값으로 넘겨주어야 가져다 쓸 수 있다.
await 1 도 비동기로 돌아간다. await 뒤가 동기라도 비동기로 돌아감
async function a() {
console.log("returns here")
const a = await 1;
console.log('a',a)
console.log('hmm')
await null
const b = await Promise.resolve(1)
console.log('b',b)
return b
}
a()
VM2797:2 returns here
VM2797:4 a 1
VM2797:5 hmm
VM2797:8 b 1
Promise {<fulfilled>: 1}
await 바로 전까지 동기로 호출 된다. 여기까지 호출되고 바로 call stack에서 빠져나오게 된다.
background로 들어가게 된 await, then 들은 promise가 resolve,reject 될때까지 대기하다가 queue에 들어가고 callstack이 비어있으면 event loop 때 올라가게 된다.
function delayP(ms) {
return new Promise((resolve,reject) => {
setTimeout(resolve,ms)
})
}
async function b() {
await delayP(3000)
await delayP(6000)
await delayP(9000)
}
async function b() {
const p1 = delayP(3000)
const p2 = delayP(6000)
await Promise.allSettled([p1,p2])
await delayP(9000)
}
1번째 b 함수는 총 3+6+9 = 18 초걸리지만
2 번째 함수 처럼 promise allsettled 를 통해 의도한대로 p2가 끝날떄까지 기다리는것을 최적화 할 수 있다. 6+9 =15
p1,p2가 서로 연관성이 없어서 뒷단에서 동시에 진행되도 무방한 경우 유용하게 사용가능하다.
Closure (함수와 함수 외부와의 관계)
function a() {
for (var i = 0; i<5; i++) {
setTimeout(() => {
console.log(i)
},i*1000)
}
}
a();
실행하면 5가 5번나오게 된다.
var의 경우 function scope를 따른다. 이때 반복문이 5번 돌면서 setTimeout이 차례대로 background로 타이머를 0,1000,2000.. 가지면서 5개가 쌓이게 된다.
이때 i = 5가 되면서 반복문을 종료하며 a도 종료되며 call stack이 텅 비게된다.
그러면 background에서 webapi 가 작업을 끝내고 Macro Queue 에 올리고 callstack이 비어있으니 console.log 함수가 callstack에 올려진다.
이떄 console.log는 i를 참조하려 한다. i는 이미 5가 되게 된다.
let으로 바꾸면 for loop의 block scope가 적용된다. i 가 a scope 에 속하지 않고 5번 생기는 for loop의 scope에 속하게 된다.
제대로 매 scope마다 1씩 증가된 값들을 참조가 가능하다.
function a() {
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(() => {
console.log(i);
}, i * 1000);
})(i);
}
}
a();
클로저 관계를 변경하여 해결하는 방법이다.
이때는 anonymous function scope 5개를 생성함으로 해결이 되는것이다.
'FrontEnd > 인간 JS 엔진되기' 카테고리의 다른 글
1. 함수호출 스택 , 스코프 체인 , 호이스팅 , this (0) | 2022.06.01 |
---|