Classic interview questions

Time:2020-9-16

I talked about it last timepromiseBasic concepts and usage, today combined with the content of the previous issue, explain several classic related interview questions.

Promise basic rules:

1. First of allPromiseThe constructor executes immediately, andPromise.then()The internal code is executed immediately at the end of the current event loop (microtask).

2. promiseOnce the state ofpendingBecome a successfulfilledOr failurerejected。 So nowpromiseIs marked as complete and does not change the state again.

3. resolveFunction sumrejectFunctions will be currentPromiseThe status is changed to complete and the asynchronous result, or error result, is returned as a parameter.

4. Promise.resolve(value)

Returns a promise object whose state is determined by the given value. If the value is thenable (that is, the object with the then method), the final state of the returned promise object is determined by the execution of the then method; otherwise (the value is empty, the primitive type or the object without the then method), the returned promise object state is fulfilled, and the value is passed to the corresponding then method. In general, if you don’t know whether a value is a promise object, use the Promise.resolve (value) to return a promise object so that the value can be used as a promise object.

5. Promise.all(iterable)/Promise.race(iterable)

Simply understand, these two functions will receivepromiseThe result of the list is returned. The difference is,allIt’s waiting for allpromiseOnly when they are triggered successfully will they returnarceIf one is successful, the result will be returned. Any one of thempromiseIf the execution fails, the result of the failure will be returned directly.

6. promiseObject’s constructor is called only once,thenMethods andcatchMethods can be called multiple times, but once you have a certain result, the next call will return the result directly.

Start answering the question

Topic 1

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
    reject('error');
})
promise.then(() => {
    console.log(3);
}).catch(e => console.log(e))
console.log(4);

We can see: rule one,promiseThe code for the constructor is executed immediately,thenperhapsrejectThe code will be put into the asynchronous micro task queue and executed immediately after the macro task is finished. Rule 2:promiseOnce the status of is changed to success or failure, it will not change again, so the execution result is: 1,2,4,3. andcatchThe functions in it will no longer be executed.

Topic 2

const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
             console.log('once')
             resolve('success')
        }, 1000)
 })
promise.then((res) => {
       console.log(res)
     })
promise.then((res) => {
     console.log(res)
 })

According to rule 6,promiseThe constructor of is executed only once, andthenMethod can be called multiple times, but the second time is to directly return the result, there will be no asynchronous waiting time, so the execution result is: print in one second:once,success,success

Three topics

On the browser, what does the following program output at a time?

const p1 = () => (new Promise((resolve, reject) => {
    console.log(1);
    let p2 = new Promise((resolve, reject) => {
        console.log(2);
        const timeOut1 = setTimeout(() => {
            console.log(3);
            resolve(4);
        }, 0)
        resolve(5);
    });
    resolve(6);
    p2.then((arg) => {
        console.log(arg);
    });

}));
const timeOut2 = setTimeout(() => {
    console.log(8);
    const p3 = new Promise(reject => {
        reject(9);
    }).then(res => {
        console.log(res)
    })
}, 0)


p1().then((arg) => {
    console.log(arg);
});
console.log(10);

Event loop:javascriptThere’s one in the enforcement rulesEvent loopIn the event loop, asynchronous events will be placed in the asynchronous queue. However, the asynchronous queue can be divided into macro tasks and micro tasks. The macro tasks on the browser side generally include:Script tag, setTimeout, setinterval, setimmediate, requestanimation frame。 Micro tasks include:MutationObserver,Promise.then catch finally。 The macro task blocks the rendering process of the browser, and the micro task is executed immediately after the macro task ends and before rendering.

Back to the title, the result is: ‘1,2,10,5,6,8,9,3’. Are you right? If you are right, then you have a basic understanding of event queues, micro tasks and macro tasks.

Step 1: execute macro task, combine rule 1, output: 1, 2, 10. In this case, there are asynchronous tasks in the event looptimeOut1,timeOut2,p2.then,p1.then

Step 2: after the macro task is executedEvent LoopThe asynchronous task will be fetched from the task queue, and the micro task will be executed first, and then it will be executed successivelyp2.then,p1.then, print 5,6.

Step 3: the micro task is finished and the macro task is started, because of 2settimeoutThe waiting time is the same, so timeout2, which enters the asynchronous queue first, is executed and printed successively: 8. In the process of executing the macro task, p3.then micro task enters the queue. After the macro task is executed, the micro task will be executed. The output is 9. Then execute timeout1, output: 3.

Step 4: combine rule 6 with P2PromiseThe execution result of the object has been determined, so 4 will not be printed.

Note: innode.jsThis is not the case with the upper output, becausenode.jsThe event loop is different from the browser side.

Topic 4

Not in useasync/awaitIn this case, a set of asynchronous code functions are executed sequentially and the final result is output.

In the last article, we have talked about the use ofpromise.resolvecombinationreduceCan execute a set of asynchronous functions in sequence.

const applyAsync = (acc,val) => acc.then(val);
const composeAsync = (...dd) => x => dd.reduce(applyAsync, Promise.resolve(x));
const transformData = composeAsync(funca, funcb, funcc, funcd);
transformData(1).then(result => console.log(result,'last result')).catch(e => console.log(e));

The above code can be packaged into a tool to use, using rule 4,promise.resolveFunction, whereddIt can be a set of synchronous functions or asynchronous functions. The final result isresultInside, exception information can be captured at the end. For more specific information, check out this article:
Promise

Topic 5

Load 10 pictures in sequence. The image address is known, but at most 3 pictures are loaded at the same timepromiserealization.

const baseUrl = 'http://img.aizhifou.cn/';
const urls = ['1.png', '2.png', '3.png', '4.png', '5.png','6.png', '7.png', '8.png', '9.png', '10.png'];
const loadImg = function (url, i) {
    return new Promise((resolve, reject) => {
        try {
            //Load a picture
            let image = new Image();
            image.onload = function () {
                resolve(i)
            }
            image.onerror = function () {
                reject(i)
            };
            image.src = baseUrl + url;
        } catch (e) {
            reject(i)
        }
    })
}
function startLoadImage(urls, limits, endHandle) {
    //The promise queue that currently exists
    let promiseMap = {};
    //The loading status corresponding to the current index will be marked as true regardless of success or failure. The format is {0: true, 1: true, 2: true...}
    let loadIndexMap = {};
    //The current and loaded indexes make it easy to find the next unloaded index. In order to save performance, you can actually not use it
    let loadIndex = 0;
    const loadAImage = function () {
        //All the resources are in the asynchronous queue
        if (Object.keys(loadIndexMap).length === urls.length) {
            //All resources are loaded, or enter the loading state, and the recursion ends
            const promiseList = Object.keys(promiseMap).reduce((arr, item) => {arr.push(promiseMap[item]); return arr}, [])
            Promise.all(promiseList).then(res => {
                //If there is no loading failure, it will be executed after all loading is completed. If one of the errors occurs, the result here will be inaccurate, but this is not required by the title.
                console.log('all');
                endHandle && endHandle()
            }).catch((e) => {
                console.log('end:' + e);
            })
        } else {
            //Traverse, know that there are three promise
            while (Object.keys(promiseMap).length < limits) {
                for (let i = loadIndex; i < urls.length; i++) {
                    if (loadIndexMap[i] === undefined) {
                        loadIndexMap[i] = false;
                        promiseMap[i] = loadImg(urls[i], i);
                        loadIndex = i;
                        break;
                    }
                }
            }
            //Get the list of the currently in progress promise, and use reduce to get it from the promise map
            const promiseList = Object.keys(promiseMap).reduce((arr, item) => {arr.push(promiseMap[item]); return arr}, [])
            Promise.race(promiseList).then((index) => {
                //If one of them is loaded successfully, delete the current promise, make the promiselist less than the limit, start recursion, and load the next one
                console.log('end:' + index);
                loadIndexMap[index] = true;
                delete promiseMap[index];
                loadAImage();
            }).catch(e => {
                //Loading failure also continues
                console.log('end:' + e);
                loadIndexMap[e] = true;
                delete promiseMap[e];
                loadAImage();
            })
        }
    }
    loadAImage()
}

startLoadImage(urls, 3)

Copy the code to the Chrome browser and you can see the following results:
Classic interview questions
As you can see, all images are loaded and printed without failureall

Analysis: according to rule 5,Promise.raceMethod takes one of the parameterspromiseObject returns the result and the successful or failed function will be triggered immediately. Using this feature, we willpromiseThe queue circulates until the limit is reached. WaitraceraceThen we added another onepromiseTo use recursion to loop the process until the endpromise.allCapture the rest of the image load.

Topic 6

Write the execution results of the following functions:

Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)

According to rule 4,Promise.resolve(1)Will return onePromise objectAnd will take 1 asthenParameters of. If the parameter of. Then or. Catch is expected to be a function, the value penetration will occur if the non function is passed in. So the final output is: 1.

Topic 6

How to cancel onepromise?
At the beginning, I will feel confused when I get this question. In fact, we can use itPromise,raceFeatures of multiplePromiseWhen a state becomes complete, it will return immediately.

function wrap(p) {
        let obj = {};
        let p1 = new Promise((resolve, reject) => {
          obj.resolve = resolve;
          obj.reject = reject;
        });
        obj.promise = Promise.race([p1, p]);
        return obj;
      }

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(123);
        }, 1000);
      });
      let obj = wrap(promise);
      obj.promise.then(res => {
        console.log(res);
      });
      // obj.resolve ("request blocked");

Once the developer takes the initiative to callobj.resolve, soobj.promiseMethods will be replaced with our own methods instead of being executedlet promiseOfthenThe method and implementation are ingenious.

summary

promiseObject inJavaScriptBecause the writing method is changeable and flexible, and the methods provided are more complex and difficult to understand. With the popularity of ES6 today, the scope of use is also wide, so it will appear frequently in the interview process.

Related reading:

Promise

What is front end asynchronous? In what situations does asynchrony occur?

Do you know the HTML5 web worker standard? Can JavaScript be multithreaded?