reference

Event Loop Execution sequence

  • The entire script is initially executed as a macro task
  • During the execution, the synchronized code is executed directly, the macro task enters the macro task queue, and the micro task enters the micro task queue
  • Check the list of micro tasks after the current macro task is executed, and execute them successively until all the tasks are executed
  • Performs rendering of the browser UI thread
  • Check whether there are Web Worker tasks and execute them
  • After executing the macro task of this round, return to 2 and repeat until the macro and microtask queues are empty

1. I Promise

1. promise

const promise1 = new Promise((resolve, reject) = > {
  console.log('promise1')})console.log('1', promise1);
Copy the code

When a new Promise is encountered, promise1 in the constructor is executed, and synchronization code 1 is executed. Promise1 is not resolved or rejected, so the state is still pending

// ans
'promise1'
'1' Promise{<pending>}
Copy the code

2. promise resolve then

const promise = new Promise((resolve, reject) = > {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() = > {
  console.log(3);
});
console.log(4);
Copy the code
  • To meet anew promiseExecute the synchronization code1
  • In case ofresolve(success)That will bepromiseStatus is changed toresolvedAnd save the value
  • Continue to execute the synchronization code2
  • Jump out of thepromiseEncountered,promise.thenMicrotask, join the microtask queue
  • Execute sync code4
  • This round of macro tasks are all executed, check the micro tasks, and findpromise.thenThe microtask state is zeropending, the execution.
// ans
1 2 4 3
Copy the code

3. promise then

const promise = new Promise((resolve, reject) = > {
  console.log(1);
  console.log(2);
});
promise.then(() = > {
  console.log(3);
});
console.log(4);
Copy the code
  • Similar to problem two, except inpromiseThere is nothing in theresolveorreject
  • sopromise.thenIt does not execute, it only executes after the state has been changed.
// ans
1 2 4
Copy the code

4. promise resove then res

const promise1 = new Promise((resolve, reject) = > {
  console.log('promise1')
  resolve('resolve1')})const promise2 = promise1.then(res= > {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
Copy the code
  • From top to bottom, a new Promise is encountered and the code promise1 in the constructor is implemented

  • When the resolve function is encountered, change the state of promise1 to Resolved and save the result

  • When it encounters the promise1. Then microtask, place it on the microtask queue

Promise2 is a new Promise whose state is' pending 'Copy the code
  • Execute synchronization code 1 and print the promise1 state as Resolved

  • Execute synchronization code 2 and print the status of promise2 as pending

  • When the macro task is complete, look for the task queue and find the promise1. Then task that is resolved and execute it.

// ans
'promise1'
'1' Promise{<resolved>: 'resolve1'}
'2' Promise{<pending>}
'resolve1'
Copy the code

5. fn promise res

const fn = () = > (new Promise((resolve, reject) = > {
  console.log(1);
  resolve('success')
}))
fn().then(res= > { // fn is called before start
  console.log(res)
})
console.log('start')
Copy the code

The fn function returns a new Promise directly, and the fn function is called before start, so its contents should be executed first.

// ans
1
'start'
'success'
Copy the code

6. fn promise res2

const fn = () = >
  new Promise((resolve, reject) = > {
    console.log(1);
    resolve("success");
  });
console.log("start");
fn().then(res= > { // fn calls are executed after start
  console.log(res);
});
Copy the code

Start is printed before 1

// ans
"start"
1
"success"
Copy the code

2. Promise combined with setTimeout

1. promise setTimeout resolve

console.log('start')
setTimeout(() = > { // The macro task is placed in the next macro task queue
  console.log('time')})Promise.resolve().then(() = > {
  console.log('resolve')})console.log('end')
Copy the code
  • The entire script is initially executed as a macro task, and the synchronized code is pushed directly onto the stack for execution, so start and end are printed first.

  • SetTimout is put into the macro task queue as a macro task (next)

  • Promise.then is put into the microtask queue as a microtask

  • When the macro task is complete, check the microtask, find promise.then, and execute it

  • Next, the next macro task, setTimeout, is found and executed.

// ans
'start'
'end'
'resolve'
'time'
Copy the code

2. promise setTimeout

const promise = new Promise((resolve, reject) = > {  console.log(1);  setTimeout(() = > {    console.log("timerStart");    resolve("success");    console.log("timerEnd");  }, 0);  console.log(2); }); promise.then((res) = > {  console.log(res); });console.log(4);
Copy the code

Similar to 3.2, resolve has a setTimeout timer layer.

  • From top to bottom, encounter firstnew PromiseExecutes the code in the constructor1
  • The timer is then encountered and the function in that timer is placed in the delay queue for the next macro task
  • Execute sync code2
  • Jump out of thepromiseFunction, encounteredpromise.thenBut its state is still zeropending, it is understood that it is not executed first
  • Execute sync code4
  • After a round of the loop, the second macro task is entered and the delay queue is foundsetTimeoutTimer, execute it
  • Executed firsttimerStart“And then metresolveThat will bepromiseChange the state ofresolvedAnd save the result and put the previouspromise.thenPush into the microtask queue
  • Continue to execute the synchronization codetimerEnd
  • The macro tasks are all executed, and the microtask queue is searchedpromise.thenThis microtask, execute it.
// ans
1
2
4
"timerStart"
"timerEnd"
"success"
Copy the code

3. (1) setTimeout

setTimeout(() = > {
  console.log('timer1');
  setTimeout(() = > {
    console.log('timer3')},0)},0)
setTimeout(() = > {
  console.log('timer2')},0)
console.log('start')
Copy the code
// ans
'start'
'timer1'
'timer2'
'timer3'
Copy the code

3. (2) setTimeout promise

setTimeout(() = > {
  console.log('timer1');
  Promise.resolve().then(() = > {
    console.log('promise')})},0)
setTimeout(() = > {
  console.log('timer2')},0)
console.log('start')
Copy the code
// ans
'start'
'timer1'
'promise'
'timer2'
Copy the code

Promise.then is the micro task, which will be added to the list of micro tasks in this round, and timer timer3 is the macro task, which will be added to the next round of macro tasks.

4. Promise. resolve setTimeout nested

Promise.resolve().then(() = > {
  console.log('promise1'); / / 2
  const timer2 = setTimeout(() = > {
    console.log('timer2') / / 5
  }, 0)});const timer1 = setTimeout(() = > {
  console.log('timer1') / / 3
  Promise.resolve().then(() = > {
    console.log('promise2') / / 4})},0)
console.log('start'); / / 1
Copy the code

The timer is executed in the promise and the promise is executed in the timer;

Note that the Promise is directly resolve, whereas the previous New Promise is different.

Process analysis:

  • Initially the entire script is executed as the first macro task, which we mark as macro 1 and execute from top to bottom

  • When the promise.resolve ().then microtask is encountered, add the contents of that then to the first microtask queue as 1

  • When the timer timer1 is encountered, add it to the delay list for the next macro task, mark it as macro 2, and wait for execution (regardless of what is inside).

  • Execute the synchronization code start in macro 1

  • After the first macro task (macro 1) is executed, the first microtask queue (micro 1) is checked and there is a promise. Then the microtask needs to be executed

  • Perform print out the synchronization code promise1 in micro 1, then find timer Timer2, add it to macro 2 and mark it as macro 3

  • After the first microtask queue (micro1) is completed, the second macro task (macro2) is executed, starting with the synchronization code timer1

  • Then it encounters the promise2 microtask and adds it to the loop’s queue of microtasks marked as micro 2

  • There is no synchronization code to execute in macro 2, look for the loop microtask queue (micro 2), find promise2, execute it

  • After the second round of execution, macro 3 is executed and Timer2 is printed

// ans
'start'
'promise1'
'timer1'
'promise2'
'timer2'
Copy the code

5. promise pending setTimeout

const promise1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('success')},1000)})const promise2 = promise1.then(() = > {
  throw new Error('error!!! ')})console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() = > {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)
Copy the code

Process analysis:

  • Top to bottom, execute the first one firstnew PromiseThe function ofsetTimeoutAdd it to the next macro task list
  • Jump out of thenew PromiseMet,promise1.thenThis microtask, but its state is still zeropending, it is understood that it is not executed first
  • promise2Is a new state for thetapendingthePromise
  • Execute sync codeconsole.log('promise1'), and printed outpromise1The status ofpending
  • Execute sync codeconsole.log('promise2'), and printed outpromise2The status ofpending
  • When you hit the second timer, place it on the next macro task list
  • The first macro task completes and no microtasks need to be executed. Therefore, the second macro task is executed
  • Execute the contents of the first timer firstpromise1Change the state ofresolvedAnd save the result and put the previouspromise1.thenPush into the microtask queue
  • The timer has no other synchronization code to execute, so it executes the microtask queue of this round, i.epromise1.then, it throws an error and willpromise2Is set torejected
  • After the first timer is executed, the contents of the second timer are executed
  • Print out the'promise1'And at this timepromise1The status ofresolved
  • Print out the'promise2'And at this timepromise2The status ofrejected
// ans
'promise1' Promise{<pending>}
'promise2' Promise{<pending>}
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!! }Copy the code

6.

const promise1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve("success");
    console.log("timer1");
  }, 1000);
  console.log("What's in promise1?");
});
const promise2 = promise1.then(() = > {
  throw new Error("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() = > {
  console.log("timer2");
  console.log("promise1", promise1);
  console.log("promise2", promise2);
}, 2000);
Copy the code
// ans
"What's in promise1?"
"promise1" Promise<pending>
"promise2" Promise<pending>
"timer1"
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
"timer2"
"promise1" Promise[<resolved>:"success"]
"promise2" Promise[<rejected>: Error:error!!! ]Copy the code

Small summary 1

  1. Once a Promise status changes, it cannot be changed. (see 3.1)

  2. .then and.catch both return a new Promise. (As evidenced by 👆1.4 above)

  3. A catch catches errors that have not been caught by the upper layer, regardless of where it is connected. (see 3.2)

  4. In a Promise, any return value that is not a Promise is wrapped as a Promise object, for example return 2 is wrapped as return promise.resolve (2).

  5. A Promise’s.then or.catch can be called multiple times, but if the internal state of the Promise changes and a value is given, that value will be returned each time a.then or.catch is called. (see 3.5)

  6. .then or. Catch returns an error object that does not throw an error, so it will not be caught by subsequent.catches. (see 3.6)

  7. The value returned by. Then or. Catch cannot be the promise itself, otherwise an infinite loop will occur. (see 3.7)

  8. The parameters of.then or.catch are expected to be functions, and pass-through occurs when passing non-functions. (see 3.8)

  9. The. Then method takes two arguments, the first for the successful function and the second for the failed function. At some point you can think of catch as a shorthand for the second argument. (see 3.9)

  10. The.finally method also returns a Promise, and when the Promise ends, the callback will be executed, whether resolved or Rejected.

3. then catch finally

1. Multiple resolve and reject

const promise = new Promise((resolve, reject) = > {
    resolve("success1");
    reject("error");
    resolve("success2");
});
promise.then(res= > {
    console.log("then: ", res);
}).catch(err= > {
    console.log("catch: ", err);
})
Copy the code
// ans
"then: success1"
Copy the code

The resolve or reject constructor is valid only on the first execution; multiple calls do nothing. Confirming the first conclusion, once a Promise’s state changes, it cannot be changed.

2. Catch Catches errors that are not caught by the upper layer

const promise = new Promise((resolve, reject) = > {
  reject("error");
  resolve("success2");
});
promise
.then(res= > {
    console.log("then1: ", res);
  }).then(res= > {
    console.log("then2: ", res);
  }).catch(err= > {
    console.log("catch: ", err);
  }).then(res= > {
    console.log("then3: ", res);
  })
Copy the code
// ans
"catch: " "error"
"then3: " undefined
Copy the code

Proving the third conclusion, a catch can catch an error that has not been caught by the upper layer no matter where it is connected.

Then3 is also executed because catch() also returns a Promise, and since the Promise returns no value, it prints undefined.

3. Promise chain call

Promise.resolve(1)
	.then(res= >{
    	console.log(res)
    	return 2
})
.catch(err= > {
    return 3
})
.then(res= > {
    console.log(res)
})
Copy the code
// ans
1
2
Copy the code

A Promise can be called chained, but a Promise returns a new Promise each time it is called. Then or. Catch.

The output above prints 1 and 2 because resolve(1) is followed by the first THEN method, not the catch method, so the res in the second THEN actually returns the value of the first THEN.

And return 2 is wrapped as resolve(2).

(1) Reject (1) reject(2)

Promise.reject(1)
	.then(res= > {
    	console.log(res)
    	return 2
})
.catch(err= > {
    console.log(err)
    return 3
})
.then(res= > {
    console.log(res)
})
Copy the code
// ans
1
3
Copy the code

Reject (1) is a catch, and the res in the second THEN returns the catch.

5. The Promise constructor is executed only once

const promise = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        console.log('timer')
        resolve('success')},1000)})const start = Date.now()
promise.then(res= > {
    console.log(res, Date.now() - start)
})
promise.then(res= > {
    console.log(res, Date.now() - start)
})
Copy the code
// ans
'timer'
'success' 1001
'success' 1002
Copy the code

Of course, if you’re fast enough, it could be 1001 for both.

A Promise’s.then or.catch can be called multiple times, but here the Promise constructor is executed only once.

Or once the internal state of a promise changes and it has a value, each subsequent call to.then or.catch will get that value directly.

6. Throw an error

Promise.resolve().then(() = > {
    return new Error('error!!! ')
}).then(res= > {
    console.log("then: ", res)
}).catch(err= > {
    console.log("catch: ", err)
})
Copy the code
// ans"then: " "Error: error!!!"
Copy the code

Return any non-promise value will be wrapped as a Promise object, so return new Error(‘ Error!! Resolve (new Error(‘ Error!!! ‘)).

Of course, if you throw an error, you can use either of the following 👇 :

return Promise.reject(new Error('error!!! '));// orthrow new Error('error!!! ')
Copy the code

7. Then catch returns a value that cannot wet the promise itself

const promise = Promise.resolve().then(() = > {
    return promise
})
promise.catch(console.err)
Copy the code

The value returned by. Then or. Catch cannot be the promise itself, otherwise an infinite loop will occur.

So the result will be an error:

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
Copy the code

8. Value through

Promise.resolve(1)
	.then(2)
	.then(Promise.resolve(3))
	.then(console.log)
Copy the code

The parameters of.then or.catch are expected to be functions, and pass-through occurs when passing non-functions.

The value of resolve(1) is passed directly to the last THEN, because the first and second THEN are not functions; one is a number and the other is an object.

// ans1
Copy the code

9. Two arguments to then

Two arguments to the.then function:

The first argument is the function that handles the Promise success, and the second is the function that handles the failure.

This means that the value of promise.resolve (‘1’) goes into the successful function and the value of promise.reject (‘2’) goes into the failed function.

Promise.reject('err!!! ')
	.then((res) = > {
    	console.log('success', res)
}, (err) = > {
    	console.log('error', err)
}).catch(err= > {
    console.log('catch', err)
})
Copy the code
// ans
'error' 'error!!! '
Copy the code

It enters the second argument in then(), and if it is removed, it enters the catch() :

Promise.reject('error!!! ')
	.then((res) = > {
    	console.log('success', res)
}).catch(err= > {
    console.log('catch', err)
})
Copy the code
// ans
'catch' 'error!!! '
Copy the code

Here’s another example:

Promise.resolve()	.then(function success (res) {    	throw new Error('error!!! ')}, function fail1 (err) {    console.log('fail1', err)}).catch(function fail2 (err) {    console.log('fail2', err)})
Copy the code

Since the Promise calls resolve(),.then() should execute success(), but success() throws an error that is caught by a catch(), not by fail1.

// ans
fail2 Error: error!!!
    				at success
Copy the code

10. finally

Finally, there are three key points:

  1. The.finally() method executes regardless of the final state of the Promise object

  2. The.finally() callback does not accept any arguments, so there is no way in the.finally() function to know whether the Promise is resolved or rejected

  3. It will eventually return a previous Promise value by default, but if an exception is thrown it will return the exception’s Promise.

Promise.resolve('1')
	.then(res= > {
    console.log(res) / / '1'
})
	.finally(() = > {
    console.log('finally')})Promise.resolve('2')
	.finally(() = > {
    console.log('finally2')
    	return 'I'm the value returned by finally2'
})
	.then(res= > {
    	console.log('Then function after Finally2', res)
})
Copy the code

The.finally of both promises is executed, and even if finally2 returns a new value, its subsequent then() function receives ‘2’, so it prints:

// ans
'1'
'finally2'
'finally'
'Then function after Finally2' '2'
Copy the code

For why finally2 prints before Finally, see the parsing in the next example.

But before we do that, let’s make sure that finally throws an exception:

Promise.resolve('1')
	.finally(() = > {
    	console.log('finally1')
    	throw new Error('I'm an exception thrown in finally')
})
	.then(res= > {
    	console.log('Then function after finally', res)
})
	.catch(err= > {
    	console.log('Catch error', err)
})
Copy the code
// ans
'finally1'
'Catch error' Error: I am afinallyThe exception thrown byCopy the code

But if you return new Error(‘ I am an exception thrown in finally ‘), then function 1 after finally will be printed.

Then look at a more difficult example:

function promise1 () {
    let p = new Promise((resolve) = > {
        console.log('promise1') // 1:'promise'
        resolve('1')})return p
}
function promise2 () {
    return new Promise((resolve, reject) = > {
        reject('error')
    })
}
promise1()
	.then(res= > console.log(res)) / / 2: '1'
	.catch(err= > console.log(err))
	.finally(() = > console.log('finally1')) // 4: 'finally1'
promise2()
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // 3: Error: error
  .finally(() = > console.log('finally2')) // 5: 'finally2'
Copy the code

Execution process:

  • First, two functions are definedpromise1andpromise2Anyway, keep reading.
The 'promise1' function is called first, and then the 'new Promise' synchronization code is executed to print 'promise1'Copy the code
  • When resolve(1) is encountered, change the state of P to Resolved and save the result.

  • Now that the function content in promise1 is finished, the function is exited

  • As promise1().then() is resolved, add the promise1().then() micro-task to the list of micro-tasks (this is the first micro-task).

  • It is important to note that the code does not follow the chain call, adding.finally() first to the list of microtasks. That is because.then is a microtask, and everything that follows the chain must wait for the current microtask to complete, so we will leave.finally() alone.

  • Further down the line, the promise2() function returns a new Promise that has no synchronization code to execute, so reject(‘error’) changes the Promise status to Rejected

  • Jump out of the promise2 function and encounter promise2().catch(), add it to the current microtask queue (which is the second microtask), and the chain call will wait until the task is finished, just like.then().

  • Promise1 ().then(), execute it, print 1, then encountered. Finally () adds the microtask to the microtask list (the third microtask) and waits to execute

  • Then execute promise2(). Catch () to print an error and add finally2 to the microtask list (the fourth microtask)

  • OK, this round is complete again, but there are two new microtasks left on the list of microtasks, so finally1 and finally2 are executed.

// ans
'promise1'
'1'
'error'
'finally1'
'finally2'
Copy the code

Anything that follows a chain call will not be executed until the previous call completes.

Just as finally() waits for promise1().then() to execute before adding finally() to the microtask queue, the same is true if you replace finally() with then().

4. All and race in the Promise

.all() receives a set of asynchronous tasks, executes them in parallel, and does not execute the callback until all asynchronous operations are complete.

.race() also receives a set of asynchronous tasks and executes them in parallel, retaining only the results of the first completed asynchronous operation. The other methods still execute, but the results are discarded.

1. Promise.all()

If you define a Promise directly in a script file, the first argument to its constructor is executed immediately, like this:

const p1 = new Promise(r= > console.log('Print now'))
Copy the code

So to control when it executes, we can wrap a function around it and call it when we need it to execute:

function runP1 () {
    const p1 = new Promise(r= > console.log('Print now'))
    return p1
}
runP1() // Only executed when this function is called
Copy the code

Now build a function like this:

function runAsync (x) {
    const p = new Promise(r= > setTimeout(() = > r(x, console.log(x)), 1000))
    return p
}
Copy the code

The function passes in a value x, and then prints out the x at an interval of one second.

What if I execute it with.all()?

function runAsync (x) {
    const p = new Promise(r= > setTimeout(() = > r(x, console.log(x)), 1000))
    return p
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)])
  .then(res= > console.log(res))
Copy the code
// ans// After an interval of one second, the console prints 1, 2, 3, and an array [1, 2, 3]. 123 [1, 2, 3]
Copy the code

With ALL, you can perform multiple asynchronous operations in parallel and process all returned data in a single callback.

The. Then () callback after.all() receives the results of all asynchronous operations.

And the array order in this result is the same as the array order received by promise.all ()!!

There is a scene is very suitable for this, some game materials more applications, when opening the web page, pre-load the need to use a variety of resources such as pictures, Flash and various static files. After everything is loaded, we’ll initialize the page.

2. Catch Catches the first exception in all

Add a runReject function that rejects an error after 1000 * x seconds.

Meanwhile, the.catch() function catches the first exception in.all() and executes it only once.

function runAsync (x) {
    const p = new Promise(r= > setTimeout(() = > r(x, console.log(x)),1000))
    return p
}
function runReject (x) {
    const p = new Promise((res, rej) = > setTimeout(() = > rej(`Error: ${x}`.console.log(x)),1000*x))
    return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
	.then(res= > console.log(res)) // Because of a set of exceptions, all will not enter here
	.catch(err= > console.log(err))
Copy the code
// ans
// Output after 1s
1
3
// output after 2s
2
Error: 2
// Output after 4s
4
Copy the code

.catch is the exception that catches first, which in this case is the result of runReject(2).

In addition, if there is an exception in a set of asynchronous operations, it is not entered in the first callback argument of.then().

Notice, why don’t you say don’t enter.then()

Because!!!!! The second argument to the.then() method can also catch errors

Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  .then(res= > console.log(res), 
  err= > console.log(err))
Copy the code

3. Promise.race()

The.race() method, which only gets the results of the first completed execution. Other asynchronous tasks continue, but race does not care about the results of those tasks.

Modify the 6.1 topic

function runAsync (x) {
  const p = new Promise(r= > setTimeout(() = > r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res= > console.log('result: ', res))
  .catch(err= > console.log(err))
Copy the code
// ans
1
'result:' 1
2
3
Copy the code

What is the use of this race? There are many scenarios, such as using Race to set a timeout for an asynchronous request and perform the corresponding operation after the timeout

4.

Modify the 6.2 topic

function runAsync(x) {
  const p = new Promise(r= >
    setTimeout(() = > r(x, console.log(x)), 1000));return p;
}
function runReject(x) {
  const p = new Promise((res, rej) = >
    setTimeout(() = > rej(`Error: ${x}`.console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res= > console.log("result: ", res))
  .catch(err= > console.log(err));
Copy the code

The same goes for the error. In this case, runReject(0) runs out first, so catch() is used:

// ans
0
'Error: 0'
1
2
3
Copy the code

A small summary.then()and.race()

  • Promise.all() takes a set of asynchronous tasks, executes them in parallel, and does not execute the callback until all the asynchronous operations are complete.

  • .race() also receives a set of asynchronous tasks and executes them in parallel, retaining only the results of the first completed asynchronous operation. The other methods still execute, but the results are discarded.

  • The array order in the promise.all () result is the same as the array order received by promise.all ().

  • If the array passed by all and race contains an asynchronous task that throws an exception, only the first error thrown will be caught, either by the second argument to then or by a subsequent catch. This does not affect the execution of other asynchronous tasks in the array.

5. async/await

1. async/await/promise

async function async1() {
    console.log("async1 start")
    await sync2()
    console.log("async1 end")}async function async2() {
    console.log("async2")
}
async1();
console.log('start')
Copy the code
// ans
'async1 start'
'async2'
'start'
'async1 end'
Copy the code

Process analysis:

  • First of all, we created two functions, so let’s not look at where the function was created, but where it was called
  • foundasync1The function is called, and then you look at the content of the call
  • Executes the synchronization code in the functionasync1 startAnd then I ran into himawaitIt will blockasync1The execution of the following code is therefore executed firstasync2Synchronization code inasync2“And jump outasync1
  • Jump out of theasync1Function to execute the synchronization codestart
  • After a round of macro tasks have all been executed, execute the previous oneawaitThe restasync1 end.

It can be understood as:

The statements immediately following await are placed in the new Promise, and the next line and statements after await are placed in promise.then

Pseudocode to convert await to promise.then:

async function async1() {
  console.log("async1 start");
  // The original code
  // await async2();
  // console.log("async1 end");
  
  // The converted code
  new Promise(resolve= > {
    console.log("async2")
    resolve()
  }).then(res= > console.log("async1 end"))}async function async2() {
  console.log("async2");
}
async1();
console.log("start")
Copy the code

The difference between await and Promise, what if we replace await async2() with a new Promise?

async function async1() {
  console.log("async1 start");
  new Promise(resolve= > {
    console.log('promise')})console.log("async1 end");
}
async1();
console.log("start")
Copy the code
// ans
'async1 start'
'promise'
'async1 end'
'start'
Copy the code

New Promise() does not block the execution of the synchronous code async1 end that follows.

2. async setTimeout

Add a timer to async2 in 5.1

async function async1() {  console.log("async1 start");  await async2();  console.log("async1 end"); }async function async2() {  setTimeout(() = > {    console.log('timer')},0)  console.log("async2"); }async1();console.log("start")
Copy the code

The timer is always executed last and is placed in the delay queue for the next macro task.

// ans
'async1 start'
'async2'
'start'
'async1 end'
'timer'
Copy the code

3. Add more timers

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() = > { // The third timer
    console.log('timer1')},0)}async function async2() {
  setTimeout(() = > { // The first timer
    console.log('timer2')},0)
  console.log("async2");
}
async1();
setTimeout(() = > { // Second timer
  console.log('timer3')},0)
console.log("start")
Copy the code
// ans
'async1 start'
'async2'
'start'
'async1 end'
'timer2'
'timer3'
'timer1'
Copy the code

4 Return is not a promise

Normally, the await command in async is a Promise object that returns the result of that object.

But if it is not a Promise object, it returns the corresponding value, equivalent to promise.resolve ().

async function fn () {
  // return await 1234
  / / is equivalent to
  return 123
}
fn().then(res= > console.log(res))
Copy the code
// ans
123
Copy the code

5 await received no response

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve= > {
    console.log('promise1')})console.log('async1 success'); // Will not be executed
  return 'async1 end' // Will not be executed
}
console.log('srcipt start')
async1().then(res= > console.log(res))
console.log('srcipt end')
Copy the code

In async1, a Promise after await has no return value. That is, its state is always pending, so it is awaiting, await but no response.

So nothing after await is executed, including.then after async1.

// ans
'script start'
'async1 start'
'promise1'
'script end'
Copy the code

6. await+promise+resolve

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve= > {
    console.log('promise1')
    resolve('promise1 resolve')
  }).then(res= > console.log(res))
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res= > console.log(res))
console.log('srcipt end')
Copy the code

If there is a return value, it will execute normally

// ans
'script start'
'async1 start'
'promise1'
'script end'
'promise1 resolve'
'async1 success'
'async1 end'
Copy the code

7.

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve= > {
    console.log('promise1')
    resolve('promise resolve')})console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res= > { // This has nothing to do with the resolve value of new Promise
  console.log(res) // res should be async1 return 'async1 end'
})
new Promise(resolve= > {
  console.log('promise2')
  setTimeout(() = > {
    console.log('timer')})})Copy the code
// ans
'script start'
'async1 start'
'promise1'
'promise2'
'async1 success' // Async1 new Promise has no relation to async1().then()
'async1 end'
'timer'
Copy the code

8. Interview Question 1

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");// The first microtask
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function() { // The first timer, macro task
  console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2"); // Second microtask
});
console.log('script end')
Copy the code
// ans
'script start'
'async1 start'
'async2'
'promise1'
'script end'
'async1 end'
'promise2'
'setTimeout'
Copy the code

9.

async function testSometing() {
  console.log("Executive testSomething");
  return "testSometing";
}
async function testAsync() {
  console.log("Executive testAsync");
  return Promise.resolve("hello async");
}
async function test() {
  console.log("test start...");
  const v1 = await testSometing();
  console.log(v1); // await the statement equivalent to.then(), the first microtask, so skip ahead
  const v2 = await testAsync();
  console.log(v2); // The third microtask will be skipped
  console.log(v1, v2); // Perform the last microtask in sequence
}
test();
var promise = new Promise(resolve= > {
  console.log("promise start...");
  resolve("promise");
});
promise.then(val= > console.log(val)); // Second microtask
console.log("test end...");
Copy the code
// ans
'test start... '
'execution testSomething'
'promise start... '
'test end... '
'testSomething' // The second round takes the first microtask
'execution testAsync'
'promise' // The second microtask in the second round
'hello async' // Perform the third microtask in the third round
'testSomething' 'hello async' // Round 3 performs the last microtask in sequence
Copy the code

6. Async processing errors

1. await followed by an exception or error

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) = > {
    console.log('async2')
    reject('error')
  })
}
async1().then(res= > console.log(res))
Copy the code

If an error is thrown in an async function, the error result is terminated and execution does not proceed.

// ans
'async2'
Uncaught (in promise) error
Copy the code

Same thing if you throw new Error.

2. To ensure that the error does not affect async functions, use a try catch

async function async1 () {
    try {
        await Promise.reject('error!!! ')}catch (e) {
        console.log(e)
    }
    console.log('async1')
    return Promise.resolve('async1 success')
}
async1().then(res= > console.log(res))
console.log('script start')
Copy the code
// ans
'script start'
'error!!! '
'async1'
'async1 success'
Copy the code

Or you can simply follow promise.reject with a catch() method:

async function async1 () {
  // try {
  // await Promise.reject('error!!! ')
  // } catch(e) {
  // console.log(e)
  // }
  await Promise.reject('error!!! ')
    .catch(e= > console.log(e)) // Add a catch here
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res= > console.log(res))
console.log('script start')
Copy the code

7. The comprehensive problem

1.

const first = () = > (new Promise((resolve, reject) = > {
    console.log(3);
    let p = new Promise((resolve, reject) = > { 
        console.log(7);
        setTimeout(() = > { // The first macro task
            console.log(5);
            resolve(6); // Only the first resolve is recorded
            console.log(p)
        }, 0)
        resolve(1); // the result of p
    });
    resolve(2); // First result
    p.then((arg) = > { // The first microtask
        console.log(arg); / / 1
    });
}));
first().then((arg) = > { // Second microtask
    console.log(arg); / / 2
});
console.log(4);
Copy the code
// ans
3
7
4
1
2
5
Promise{<resolved>: 1}
Copy the code

Process analysis:

  • The first piece of code defines a function, so we have to look at where it’s executed, and find out where it is, right4Before, so check it outfirstThe contents of the function. (This step is kind of like the problem1.5)
  • functionfirstIt returns onenew Promise(), so execute the synchronization code inside first3
  • And then I met another onenew Promise(), directly execute the synchronization code inside7
  • after7Later, inp, encounters a timer, first puts it in the next macro task queue, and then goes down
  • metresolve(1)Right herepTo change the state toresolvedAnd the return value is1, but don’t do it here either
  • Jump out of thep, metresolve(2)Here,resolve(2), means thefirstThe one returned by the functionPromiseIf your status changes, ignore it.
  • And then I ran intop.then, add it to the list of microtasks for the loop, and wait for execution
  • Jump out of thefirstDelta function, I’ve met itfirst().then(), add it to the list of microtasks for this cycle (p.thenThe following execution)
  • The synchronization code is then executed4
  • The synchronization codes of this round are all executed, and the list of microtasks is searched and foundp.thenandfirst().then(), execute in turn, print out1 and 2
  • When this round of task is completed, it is found that there is still a timer that has not run out, and then the content of this timer is executed, and the synchronization code is executed5
  • And then I met another oneresolve(6), it is placed inpIn, butpThe state of theta has changed before, so it’s not going to change here, in other wordsresolve(6)It’s kind of useless, so it’s printed outpforPromise{<resolved>: 1}. (This step is similar to the problem3.1)

2.

const async1 = async() = > {console.log('async1');
  setTimeout(() = > { // The first macro task
    console.log('timer1')},2000)
  await new Promise(resolve= > {
    console.log('promise1')})console.log('async1 end') // await is equivalent to.then(), first microtask, but !!!! Await returns no value, then nothing else is executed
  return 'async1 success' // This return is not in await, so await has no return value
} 
console.log('script start');
async1().then(res= > console.log(res)); // Second microtask
console.log('script end');
Promise.resolve(1) 
  .then(2) // then expectation is a function, 2 is not a function, penetration
  .then(Promise.resolve(3))
  .catch(4)
  .then(res= > console.log(res)) / / 1
setTimeout(() = > {
  console.log('timer2') // Second macro task
}, 1000)
Copy the code
// ans
'script start'
'async1'
'promise1'
'script end'
1
// 'async1 end' // Since await has no return value, nothing after await is executed
// 'async1 success' // because async has no return value
'timer1'
'timer2'
Copy the code
  • asyncIn the functionawaitthenew PromiseIf there is no return value, nothing is done5.5)
  • .thenParameters in a function are expected to be functions, and pass-through occurs if they are not3.8 )

3.

const p1 = new Promise((resolve) = > {
  setTimeout(() = > { // The first macro task
    resolve('resolve3');
    console.log('timer1')},0)
  resolve('resovle1'); // The resolve state is resolve1
  resolve('resolve2'); // No new state changes are received
}).then(res= > {
  console.log(res) // The first microtask
  setTimeout(() = > { // Second macro task
    console.log(p1) // The return value of.finally, which was the last Promise, is undefined for.then()
  }, 1000)
}).finally(res= > { // Second microtask
  console.log('finally', res)
})
Copy the code
// ans
'resolve1'
'finally' undefined
'timer1'
Promise{<resolve>: undefined}
Copy the code

Note:

  • Once the state of a Promise changes, it cannot be changed (similar to item 3.5)

  • Finally () is a decoy (similar to 3.10) because the res in finally() is a decoy (similar to 3.10) because the finally() callback will not accept the Promise and will execute regardless of whether the Promise state is resolved or Rejected.

  • The last timer prints p1, which is the return value of.finally, We know that the return value of.finally, if no errors are thrown, defaults to the return value of the last Promise (also mentioned in 3.10). In this case, the last Promise was.then(), but this.then() does not return a value. So p1’s printed Promise will be undefined, and if you add a return 1 to the bottom of the timer, it will change to 1

8. Big Factory interview questions

1. Use promise to print 1,2, and 3 every second

I Promise to reduce it

const arr = [1.2.3]
arr.reduce((p, x) = > {
    return p.then(() = > {
        return new Promise(r= > {
            setTimeout(() = > r(console.log(x)), 1000)})})},Promise.resolve())
Copy the code

2. Use Promise to realize alternating and repeated light of traffic lights

Red light 3 seconds on once, yellow light 2 seconds on once, green light 1 second on once; How do I make three lights turn on again and again? (Implemented with Promise) Three lighting functions already exist:

// ans
function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}
const light = function (timer, cb) {
    return new Promise(resolve= > {
        setTimeout(() = >{
            cb()
            resolve()
        }, timer)
    })
}
const step = function () {
    Promise.resolve().then(() = > {
        return light(3000, red)
    }).then(() = > {
        return light(2000, green)
    }).then(() = > {
        return light(1000, yellow)
    }).then(() = > {
        return step()
    })
}
step();
Copy the code

3. Encapsulate a method for loading images asynchronously

In the image onload function, use resolve

function loadImg(url) {
    return new Promise((resolve, reject) = > {
        const img = new Image();
        img.onload() = function() {
            console.log("Image loading completed")
            resolve(img)
        }
        img.onerror = function() {
            reject(new Error('Could not load image at ' + url))
        }
        img.src = url
    })
}
Copy the code