ES6 Promise practical written test

ES6 Promise exercise

As the saying goes, it's Wang Ba who doesn't move. It's time to show his skill after learning Promise so long in the last article!

Basic questions

Topic 1
const promise = new Promise((resolve, reject) => {
    console.log(1)
    resolve()
    console.log(2)
})
promise.then(() => {
    console.log(3)
})
console.log(4)

Analysis:
The Promise constructor is executed synchronously, and the functions in promise.then are executed asynchronously.

Operation result:

// => 1
// => 2
// => 4
// => 3
Topic two
const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

Analysis:
This question mainly understands js execution mechanism.

In the first round of event cycle, execute the macro task first, main script, new Promise execute immediately, output 3, execute the new Promise operation of p, output 7, find setTimeout, put the callback function into the next round of event queue, then of p, temporarily named then1, put it into the micro task queue, and first also has then, named then2, put it into the micro task queue. Execute console.log(4), output 4, and the macro task is finished.

Then execute the micro task, execute then1, output 1, execute then2, output 3

The first cycle of events ends and the second cycle begins. In the second round of event cycle, execute the callback in the macro task first, that is, setTimeout. Output 5.resolve(6) will not take effect, because once the Promise state of p changes, it will not change again.

Operation result:

// => 3
// => 7
// => 4
// => 1
// => 2
// => 5
Topic three
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)

Operation result:

promise1 Promise {<pending>}
promise2 Promise {<pending>}
Uncaught (in promise) Error: error!!!
    at <anonymous>
promise1 Promise {<resolved>: "success"}
promise2 Promise {<rejected>: Error: error!!!
    at <anonymous>}

Explanation: project has three states: pending, fulfilled, or rejected. The status change can only be pending - > completed or pending - > rejected. Once the status changes, it cannot be changed again. Promise 2 above is not promise 1, but a new promise instance returned.

Topic four
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)
  })

Analysis:
The resolve or reject in the constructor is valid only for the first execution. Multiple calls have no effect. Echo the conclusion of code 2: once the project state is changed, it cannot be changed again.

Operation result:

then: success1
Topic five
Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((res) => {
    console.log(res)
  })

Analysis:
Promise can be chain called. When it comes to chained calls, we usually think of using return this, but promise does not. Promise returns a new promise every time. then or. catch is called, which implements the chain call.

Operation result:

1
2
Topic six
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    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)
})

Analysis:
The. Then or. catch of promise can be called multiple times, but here the promise constructor executes only once. In other words, once the internal state of promise is changed and there is a value, then every subsequent call to. Then or. catch will get the value directly.

Operation result:

once
success 1005
success 1007
Topic seven
Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })

Analysis:
An error object return ed in. then or. catch will not throw an error, so it will not be captured by subsequent. catch. You need to change it to one of the following:

return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')

Because any non promise value returned will be wrapped as promise object, that is, return new Error('error!!! ') is equivalent to return Promise.resolve(new Error('error!!!').

Operation result:

then:  Error: error!!!
    at <anonymous>
Topic eight
const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(console.error)

Parsing: the value returned by. then or. catch cannot be promise itself, otherwise it will cause a dead cycle. Be similar to:

process.nextTick(function tick () {
  console.log('tick')
  process.nextTick(tick)
})

Operation result:

TypeError: Chaining cycle detected for promise #<Promise>
Topic nine
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

Analysis:
The parameter of. then or. catch is expected to be a function, and value penetration occurs when passing in a non function.

Operation result:

1
Topic ten
Promise.resolve()
  .then(function success (res) {
    throw new Error('error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .catch(function fail2 (e) {
    console.error('fail2: ', e)
  })

Parsing:. then can receive two parameters. The first is to process the successful function, and the second is to process the wrong function. . catch is a simple way to write. then's second parameter, but there is one thing we need to pay attention to in their usage. then's second error handling function can't catch the error thrown by the first successful function, while the subsequent. Catch can catch the previous error. Of course, the following code can also be used:

Promise.resolve()
  .then(function success1 (res) {
    throw new Error('error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .then(function success2 (res) {
  }, function fail2 (e) {
    console.error('fail2: ', e)
  })

Operation result:

fail2:  Error: error
    at success (<anonymous>)
Topic eleven
process.nextTick(() => {
  console.log('nextTick')
})
Promise.resolve()
  .then(() => {
    console.log('then')
  })
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')

Analysis:
Both process.nextTick and promise.then belong to microtask, while setImmediate belongs to macrotask, which is executed in the check phase of the event loop. Microtasks are executed between each stage of the event loop, and the beginning of the event loop is executed once.

Operation result:

end
nextTick
then
setImmediate

Written examination questions

The above questions are too basic and not challenging? Then something real!

Programming questions

The red light is on once every 3 seconds, the green light is on once every 1 second, and the yellow light is on once every 2 seconds. How to use Promise to keep the three lights on alternately? (Hai Kangwei's pen test)

function red(){
    console.log('red');
}
function green(){
    console.log('green');
}
function yellow(){
    console.log('yellow');
}

Analysis:
First look at the problem. The problem requires that after the red light is on, the green light is on. After the green light is on, the yellow light is on. After the yellow light is on, the red light is on So how to implement it through Promise?

In other words, when the red light is on, Promise to turn on the green light after 2s, when the green light is on, Promise to turn on the yellow light after 1s, when the yellow light is on, Promise to turn on the red light after 3s This is obviously a Promise chain call. You may have a thought in mind here. We need to write every light up action in the then() method, return a new Promise at the same time, and set its status from pending to fulfilled, allowing the next light to be on.

function red() {
  console.log('red');
}

function green() {
  console.log('green');
}

function yellow() {
  console.log('yellow');
}


let myLight = (timer, cb) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      cb();
      resolve();
    }, timer);
  });
};


let myStep = () => {
  Promise.resolve().then(() => {
    return myLight(3000, red);
  }).then(() => {
    return myLight(2000, green);
  }).then(()=>{
    return myLight(1000, yellow);
  }).then(()=>{
    myStep();
  })
};
myStep();

// output:
// => red
// => green
// => yellow
// => red
// => green
// => yellow
// => red
Programming questions two

Please implement a mergePromise function to execute the passed array in sequence, and put the returned data into the array data in sequence.

const timeout = ms => new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, ms);
});

const ajax1 = () => timeout(2000).then(() => {
    console.log('1');
    return 1;
});

const ajax2 = () => timeout(1000).then(() => {
    console.log('2');
    return 2;
});

const ajax3 = () => timeout(2000).then(() => {
    console.log('3');
    return 3;
});

const mergePromise = ajaxArray => {
    // Implement your code here

};

mergePromise([ajax1, ajax2, ajax3]).then(data => {
    console.log('done');
    console.log(data); // data is [1, 2, 3]
});

// Require output separately
// 1
// 2
// 3
// done
// [1, 2, 3]

Analysis:
This problem mainly focuses on using Promise to control asynchronous process. First, ajax1, ajax2 and ajax3 are all functions, but after these functions are executed, they will return a Promise. According to the requirements of the problem, as long as the three functions are executed in order, then put the results into data;

Answer:

const mergePromise = ajaxArray => {
  // Implement your code here
  // Save the result of function execution in array
  var data = [];

  // The Promise.resolve method calls without parameters, and directly returns a Promise object in the resolved state.
  var sequence = Promise.resolve();

  ajaxArray.forEach(item => {
    // The first then method is used to execute each function in the array,
    // The second then method takes the result of the function in the array,
    // And add the result to the data, and then return the data.
    sequence = sequence.then(item).then(res => {
      data.push(res);
      return data;
    });
  });

// After traversal, return a Promise, that is, sequence. Its [[PromiseValue]] value is data,
// data (the result of saving the function execution in the array) will also be passed into the next call of the then method as a parameter.
  return sequence;
};
Programming questions three

The urls of the existing eight image resources have been stored in the array urls, and there is a function function loading. Enter a url link and return a Promise. The Promise resolve s when the image download is completed, and reject s if the download fails. Requirement: no more than 3 links can be downloaded at any time.
Please write a piece of code to fulfill this requirement, and download all pictures as quickly as possible.

var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://www.kkkk1000.com/images/wxQrCode2.png'];

function loadImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image()
        img.onload = () => {
            console.log('One image loading completed');
            resolve();
        }
        img.onerror = reject;
        img.src = url;
    })
};

analysis
The title means that three pictures need to be requested simultaneously first. When a picture is loaded, it will continue to initiate a picture request, keeping the number of concurrent requests at three until all the pictures need to be loaded.

To implement with promise is to request three image resources simultaneously, so that three promises can be obtained, forming an array promises, and then calling Promise.race to return promise that changes the state the fastest, then deleting the promise object from the array promises, adding a new promise until all the URLs are taken, and finally using Promise.all to process it again Promise without changing state in array promises

function limitLoad(urls, handler, limit) {
  // Make a copy of the array
    const sequence = [...urls];

  let promises = [];

  //Concurrent requests to maximum
  promises = sequence.splice(0, limit).map((url, index) => {
    // The index returned here is the token of the task in promises, which is used to find the completed task token after Promise.race
    return handler(url).then(() => {
      return index;
    });
  });

  // Using the reduce method of array to execute in the form of queue
  return sequence.reduce((last, url, currentIndex) => {
    return last.then(() => {
      // Return Promise with the fastest status change
      return Promise.race(promises)
    }).catch(err => {
      // The catch here is not only used to catch the errors thrown by the previous then method
      // It is more important to prevent the whole chain call from being interrupted
      console.error(err)
    }).then((res) => {
      // Replace the fastest changing Promise with a new Promise
      promises[res] = handler(sequence[currentIndex]).then(() => {
        return res
      });
    })
  }, Promise.resolve()).then(() => {
    return Promise.all(promises)
  })

}

limitLoad(urls, loadImg, 3);

/*
Because the limitLoad function also returns a Promise, you can continue the chain call after all the pictures are loaded

limitLoad(urls, loadImg, 3).then(() => {
    console.log('All pictures are loaded ');
}).catch(err => {
    console.error(err);
})
*/
Programming questions four

Encapsulate a method of loading pictures asynchronously
Analysis:
It's not hard!

function loadImageAsync(url) {
    return new Promise(function(resolve,reject) {
        var image = new Image();
        image.onload = function() {
            resolve(image) 
        };
        image.onerror = function() {
            reject(new Error('Could not load image at' + url));
        };
        image.src = url;
     });
}

End

In this part, I collected some practice questions from the Internet. I didn't think it was so easy to do several programming questions for the first time, but I got a lot of benefits after finishing them, which deepened my understanding of Promise features and how to use Promise better. Therefore, the real learning of Promise should be combined with the actual development and application of specific scenarios.

Promise seems to be simple, but I found that promise has a lot of content after careful sorting. I spent a whole day looking up, learning, understanding, running code, and then sorting out promise's notes.

Thank you for reading!

Recommended reading:
[Topic: the way to advanced JavaScript]
Deep understanding closure of JavaScript
ES6 tail call and tail recursion
Summary of Git common commands
call() understanding of JavaScript
Object properties of JavaScript

I am Cloudy, a young front-end siege lion. I love research, technology and sharing.
Personal notes are not easy to organize. Thank you for reading, praising and collecting.
If you have any questions, you are welcome to point out, and also welcome to exchange front-end issues!

Tags: Javascript Programming git

Posted on Wed, 06 Nov 2019 22:24:56 -0800 by ale1981