I remember an interview with a abused question, promise complete guide

Time:2021-10-24

Author: Adrian Mejia
Translator: front end Xiaozhi
Source: adrianmjia

Like it and see it again, wechat searchGreat Migrations , station B pays attentionFront end XiaozhiThis person has no big factory background, but has an upward positive attitude. this paperGitHub https://github.com/qq44924588…It has been included in the, the articles have been classified, and a lot of my documents and tutorial materials have been sorted out.

Recently, a Vue component has been open-source, which is not perfect enough. Welcome to improve it together. I also hope you can give star support. Thank you.

GitHub address:https://github.com/qq44924588…

This article is a comprehensive tutorial on JavaScript promises. It introduces the necessary methods, such asthencatchandfinally。 It also includes dealing with more complex situations, such as withPromise.allParallel executionPromise, passPromise.raceTo handle request timeouts, promise chains, and some best practices and common pitfalls.

1.JavaScript Promises

PromiseIs an object that allows us to handle asynchronous operations. It is an alternative to Es5 early callbacks.

Compared with callback,PromiseIt has many advantages, such as:

  • Make asynchronous code easier to read.
  • Provides combined error handling.
    *Better process control allows asynchronous parallel or serial execution.

Callbacks are more likely to form deeply nested structures (also known as callbacks)Callback hell)。 As follows:

a(() => {
  b(() => {
    c(() => {
      d(() => {
        // and so on ...
      });
    });
  });
});

If you convert these functions toPromise, you can link them to generate more maintainable code. like this:

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error);

In the above example, the promise object exposes.thenand.catchMethods, which we will explore later.

1.1 how to convert the existing callback API to promise?

We can use the promise constructor to convert the callback to promise.

The promise constructor accepts a callback with two parametersresolveandreject

  • Resolve: is the callback that should be called when the asynchronous operation completes.
  • Reject: is the callback function to call when an error occurs.

The constructor immediately returns an object, namelyPromiseexample. When used in promise instances.thenMethod, you can be notified when promise is “finished”. Let’s look at an example.

Promise is just a callback?

Not at all. Promises aren’t just callbacks, but they do.thenand.catchMethod uses an asynchronous callback. Promise is an abstraction over callbacks. We can link multiple asynchronous operations and handle errors more gracefully. Let’s see its actual effect.

Promise negative pattern (promises hell)

a(() => {
  b(() => {
    c(() => {
      d(() => {
        // and so on ...
      });
    });
  });
});

Do not convert the above callback to the following promise form:

a().then(() => {
  return b().then(() => {
    return c().then(() => {
      return d().then(() =>{
        // ⚠️ Please never ever do to this! ⚠️
      });
    });
  });
});

The above conversion also forms promise hell. Don’t turn like this. Instead, it would be better to do the following:

a()
  .then(b)
  .then(c)
  .then(d)

overtime

What do you think is the output of the following program?

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('time is up ⏰');
  }, 1e3);

  setTimeout(() => {
    reject('Oops 🔥');
  }, 2e3);
});

promise
  .then(console.log)
  .catch(console.error);

Yes output:

time is up ⏰
Oops! 🔥

Or output:

time is up ⏰

The latter, because when a promiseresolvedAfter that, it can no longer be usedrejected

Once you call a method(resolveorreject), the other method will fail becausepromiseIn a stable state. Let’s explore onepromiseAll different states of.

1.2 promise status

Promise can be divided into four states:

  • ⏳ Pending: initial state, asynchronous operation is still in progress.
  • ✅ Completed: the operation is successful, and it calls.thenCallback, for example.then(onSuccess)
  • ⛔ Rejected: the operation failed, it called.catchor.thenThe second parameter of the, if any. for example.catch(onError)or.then(..., onError)
  • 😵 Settled: This is the final state of promise. Promise is dead. There is no other way to solve or refuse..finallyMethod is called.

I remember an interview with a abused question, promise complete guide

Everyone said that there was no project in the resume, so I helped you find a project and attached it[building tutorial]

1.3 promise instance method

Promise API discloses three main methods:thencatchandfinally。 Let’s discuss it with examples one by one.

Promise then

thenMethod lets you be notified when an asynchronous operation succeeds or fails. It contains two parameters, one for successful execution and the other for errors.

promise.then(onSuccess, onError);

You can also usecatchTo handle errors:

promise.then(onSuccess).catch(onError);

Promise chain

thenReturn a new promise so that multiple promises can be linked together. Like the following example:

Promise.resolve()
  .then(() => console.log('then#1'))
  .then(() => console.log('then#2'))
  .then(() => console.log('then#3'));

Promise.resolveTreat promise as a success immediately. Therefore, all of the following will be called. The output will be

then#1
then#2
then#3

Promise catch

Promise .catchMethod takes a function as an argument to handle an error. If there is no error, it will never be calledcatchmethod.

Suppose we have the following commitments:1Seconds later, parse or reject and print out their letters.

const a = () => new Promise((resolve) => setTimeout(() => { console.log('a'), resolve() }, 1e3));
const b = () => new Promise((resolve) => setTimeout(() => { console.log('b'), resolve() }, 1e3));
const c = () => new Promise((resolve, reject) => setTimeout(() => { console.log('c'), reject('Oops!') }, 1e3));
const d = () => new Promise((resolve) => setTimeout(() => { console.log('d'), resolve() }, 1e3));

Please note that,cusereject('Oops!')Simulated rejection.

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)

The output is as follows:

I remember an interview with a abused question, promise complete guide

In this case, you can seeabandcError message on.

We can usethenFunction to handle the error. But, please note,catchWill no longer be performed.

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d, () => console.log('c errored out but no big deal'))
  .catch(console.error)

I remember an interview with a abused question, promise complete guide

Because we are dealing with.then(..., onError)Part of the error, so it was not calledcatchdWill not be called. If you want to ignore the error and continue the promise chain, you cancAdd a previouscatch。 like this:

Promise.resolve()
  .then(a)
  .then(b)
  .then(() => c().catch(() => console.log('error ignored')))
  .then(d)
  .catch(console.error)

I remember an interview with a abused question, promise complete guide

Of course, this premature capture of errors is not good, because it is easy to ignore some potential problems during debugging.

Promise finally

finallyMethod is only in promise statesettledCalled only when.

If you want a piece of code to always be executed even if there are errors, you can.catchLater use.then

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)
  .then(() => console.log('always called'));

Or you can use.finallykeyword:

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)
  .finally(() => console.log('always called'));

1.4 promise class method

We can use it directlyPromiseObject.

  • Promise.all
  • Promise.reject
  • Promise.resolve
  • Promise.race

Promise.resolve and promise.reject

These are help functions that allow promise to resolve or reject immediately. You can pass a parameter as the next parameter.thenReception of:

Promise.resolve('Yay!!!')
  .then(console.log)
  .catch(console.error)

It will outputYay!!!

Promise.reject('Oops 🔥')
  .then(console.log)
  .catch(console.error)

usePromise.allExecute multiple promises in parallel

Usually, promise is executed one by one, but you can also use them in parallel.

Suppose you poll data from two different APIs. If they are not relevant, we can use themPromise.all()Both requests are triggered at the same time.

In this example, the main function is to convert US dollars to euros. We have two independent API calls. A method forBTC/USDThe other is used to obtainEUR/USD。 As you might expect, both API calls can be called in parallel. However, we need a way to know when to complete the final price calculation at the same time. We can usePromise.all, it is usually used after starting multiple asynchronous tasks to run concurrently and creating commitments for their results, so that people can wait for all tasks to complete.

const axios = require('axios');

const bitcoinPromise = axios.get('https://api.coinpaprika.com/v1/coins/btc-bitcoin/markets');
const dollarPromise = axios.get('https://api.exchangeratesapi.io/latest?base=USD');
const currency = 'EUR';

// Get the price of bitcoins on
Promise.all([bitcoinPromise, dollarPromise])
  .then(([bitcoinMarkets, dollarExchanges]) => {
    const byCoinbaseBtc = d => d.exchange_id === 'coinbase-pro' && d.pair === 'BTC/USD';
    const coinbaseBtc = bitcoinMarkets.data.find(byCoinbaseBtc)
    const coinbaseBtcInUsd = coinbaseBtc.quotes.USD.price;
    const rate = dollarExchanges.data.rates[currency];
    return rate * coinbaseBtcInUsd;
  })
  .then(price => console.log(`The Bitcoin in ${currency} is ${price.toLocaleString()}`))
  .catch(console.log);

As you can see,Promise.allAccepted a series of promises. When both requests are completed, we can calculate the price.

Let’s give another example:

const a = () => new Promise((resolve) => setTimeout(() => resolve('a'), 2000));
const b = () => new Promise((resolve) => setTimeout(() => resolve('b'), 1000));
const c = () => new Promise((resolve) => setTimeout(() => resolve('c'), 1000));
const d = () => new Promise((resolve) => setTimeout(() => resolve('d'), 1000));

console.time('promise.all');
Promise.all([a(), b(), c(), d()])
  .then(results => console.log(`Done! ${results}`))
  .catch(console.error)
  .finally(() => console.timeEnd('promise.all'));

How long will it take to solve these promises? Five seconds? One second? Or two seconds?

This is for you to verify.

Promise race

Promise.race(iterable)Method returns a promise. Once a promise in the iterator is resolved or rejected, the returned promise will be resolved or rejected.

const a = () => new Promise((resolve) => setTimeout(() => resolve('a'), 2000));
const b = () => new Promise((resolve) => setTimeout(() => resolve('b'), 1000));
const c = () => new Promise((resolve) => setTimeout(() => resolve('c'), 1000));
const d = () => new Promise((resolve) => setTimeout(() => resolve('d'), 1000));

console.time('promise.race');
Promise.race([a(), b(), c(), d()])
  .then(results => console.log(`Done! ${results}`))
  .catch(console.error)
  .finally(() => console.timeEnd('promise.race'));

What is the output?

outputb。 usePromise.race, the first execution will complete and the last result will be returned.

You might ask:Promise.raceWhat is the purpose of the?

I don’t use it often. However, in some cases, it can come in handy, such as timing requests or batch processing request arrays.

Promise.race([
  fetch('http://slowwly.robertomurray.co.uk/delay/3000/url/https://api.jsonbin.io/b/5d1fb4dd138da811182c69af'),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('request timeout')), 1000))
])
.then(console.log)
.catch(console.error);

I remember an interview with a abused question, promise complete guide

If the request is fast enough, you will get the result of the request.

I remember an interview with a abused question, promise complete guide

Everyone said that there was no project in the resume, so I helped you find a project and attached it[building tutorial]

1.5 promise FAQs

Execute promise serially and pass parameters

This time, we will do some research on nodefsUse the promises API and connect the two files:

const fs = require('fs').promises; // requires node v8+

fs.readFile('file.txt', 'utf8')
  .then(content1 => fs.writeFile('output.txt', content1))
  .then(() => fs.readFile('file2.txt', 'utf8'))
  .then(content2 => fs.writeFile('output.txt', content2, { flag: 'a+' }))
  .catch(error => console.log(error));

In this example, we read file 1 and write it tooutputFile. Later, we read the file 2 and attach it to theoutputFile. As you can see,writeFilePromise returns the contents of the file, which you can in the nextthenClause.

How to link multiple conditional commitments?

You may want to skip specific steps on the promise chain. There are two ways to do this.

const a = () => new Promise((resolve) => setTimeout(() => { console.log('a'), resolve() }, 1e3));
const b = () => new Promise((resolve) => setTimeout(() => { console.log('b'), resolve() }, 2e3));
const c = () => new Promise((resolve) => setTimeout(() => { console.log('c'), resolve() }, 3e3));
const d = () => new Promise((resolve) => setTimeout(() => { console.log('d'), resolve() }, 4e3));

const shouldExecA = true;
const shouldExecB = false;
const shouldExecC = false;
const shouldExecD = true;

Promise.resolve()
  .then(() => shouldExecA && a())
  .then(() => shouldExecB && b())
  .then(() => shouldExecC && c())
  .then(() => shouldExecD && d())
  .then(() => console.log('done'))

If you run the code example, you will notice that onlyaanddWas carried out as expected.

Another way is to create a chain and then add them only if:

const chain = Promise.resolve();

if (shouldExecA) chain = chain.then(a);
if (shouldExecB) chain = chain.then(b);
if (shouldExecC) chain = chain.then(c);
if (shouldExecD) chain = chain.then(d);

chain
  .then(() => console.log('done'));

How to limit parallel promise?

To do this, we need to limit it in some wayPromise.all

Suppose you have many concurrent requests to execute. If usedPromise.allIs bad (especially when the API is rate limited). Therefore, we need a method to limit the number of promises, which we callpromiseAllThrottled

// simulate 10 async tasks that takes 5 seconds to complete.
const requests = Array(10)
  .fill()
  .map((_, i) => () => new Promise((resolve => setTimeout(() => { console.log(`exec'ing task #${i}`), resolve(`task #${i}`); }, 5000))));

promiseAllThrottled(requests, { concurrency: 3 })
  .then(console.log)
  .catch(error => console.error('Oops something went wrong', error));

The output should look like this:

I remember an interview with a abused question, promise complete guide

The above code limits concurrency to parallel execution3A task.

realizationpromiseAllThrottled One way is to usePromise.raceTo limit the number of active tasks at a given time.

/**
 * Similar to Promise.all but a concurrency limit
 *
 * @param {Array} iterable Array of functions that returns a promise
 * @param {Object} concurrency max number of parallel promises running
 */
function promiseAllThrottled(iterable, { concurrency = 3 } = {}) {
  const promises = [];

  function enqueue(current = 0, queue = []) {
    // return if done
    if (current === iterable.length) { return Promise.resolve(); }
    // take one promise from collection
    const promise = iterable[current];
    const activatedPromise = promise();
    // add promise to the final result array
    promises.push(activatedPromise);
    // add current activated promise to queue and remove it when done
    const autoRemovePromise = activatedPromise.then(() => {
      // remove promise from the queue when done
      return queue.splice(queue.indexOf(autoRemovePromise), 1);
    });
    // add promise to the queue
    queue.push(autoRemovePromise);

    // if queue length >= concurrency, wait for one promise to finish before adding more.
    const readyForMore = queue.length < concurrency ? Promise.resolve() : Promise.race(queue);
    return readyForMore.then(() => enqueue(current + 1, queue));
  }

  return enqueue()
    .then(() => Promise.all(promises));
}

promiseAllThrottledDeal with promises one-on-one. Its executionPromisesAnd add it to the queue. If the queue is less than the concurrency limit, it will continue to be added to the queue. When the limit is reached, we usePromise.raceWait for a commitment to complete, so you can replace it with a new commitment. The trick here is that promise will be automatically deleted from the queue after it is completed automatically. In addition, we useraceTo detect when promise is completed and add a new promise.

Talents[Sanlian]This is the biggest driving force for Xiaozhi to share constantly. If there are any mistakes and suggestions in this blog, talents are welcome to leave messages. Finally, thank you for watching.


The bugs that may exist after code deployment cannot be known in real time. Afterwards, in order to solve these bugs, we spent a lot of time on log debugging. By the way, we recommend a useful bug monitoring toolFundebug

Original text:https://adrianmejia.com/promi…

communication

The article is continuously updated every week. You can search “Daqian world” on wechat to read and urge more for the first time (one or two articles earlier than the blog). This article GitHubhttps://github.com/qq449245884/xiaozhiI have collected many documents and I have welcomed Star and perfect. You can review the test points and pay attention to the official account.welfare, you can see the benefits, you know.

I remember an interview with a abused question, promise complete guide

Recommended Today

SQL exercise 20 – Modeling & Reporting

This blog is used to review and sort out the common topic modeling architecture, analysis oriented architecture and integration topic reports in data warehouse. I have uploaded these reports to GitHub. If you are interested, you can have a lookAddress:https://github.com/nino-laiqiu/TiTanI recorded a relatively complete development process in my hexo blog deployed on GitHub. You can […]