Sign in
Log inSign up

Top 10 Promise Way to Build and Level Up your Coding Skills

Jay Chow's photo
Jay Chow
·Mar 6, 2020

There are many great ways to improve your coding skills, experiment with new technologies. The truth of the matter is to understand JavaScript and Promise.

Part 1:

const prom = new Promise((res, rej) => {
  console.log('first');
  res();
  console.log('second');
});
prom.then(() => {
  console.log('third');
});
console.log('fourth');

// first
// second
// fourth
// third

Promise executes synchronously, promise.then executes asynchronously

Part 2:

const prom = new Promise((res, rej) => {
  setTimeout(() => {
    res('success');
  }, 1000);
});
const prom2 = prom.then(() => {
  throw new Error('error');
});

console.log('prom', prom);
console.log('prom2', prom2);

setTimeout(() => {
  console.log('prom', prom);
  console.log('prom2', prom2);
}, 2000);

// prom 
// Promise {<pending>}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "success"

// prom2
// Promise {<pending>}__proto__: 
// Promise[[PromiseStatus]]: "rejected"[[PromiseValue]]: 
// Error: error
//     at <anonymous>:7:9

promise has three different states:

  • pending
  • fulfilled
  • rejected

Once the status updated, pending->fulfilled or pending->rejected, it can be changed again. The prom1 is different from prom2 and both of them return new Promise status.

Part 3:

const prom = new Promise((res, rej) => {
  res('1');
  rej('error');
  res('2');
});

prom
  .then(res => {
    console.log('then: ', res);
  })
  .catch(err => {
    console.log('catch: ', err);
  });

// then: 1

The resolve or reject only execute once even there is a resolve call after the reject. It won't execute.

Part 4:

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });

// 1
// 2

Promises can be chained. When referring to chained calls, we usually think of returning this, but Promises do not. Each time a promise calls .then or .catch, a new promise will be returned, thus implementing chained calls.

Part 5:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('first')
    resolve('second')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start, "third")
})
promise.then((res) => {
  console.log(res, Date.now() - start, "fourth")
})

// first
// second 1054 third
// second 1054 fourth

A promise .then or .catch can be called multiple times, but here the Promise constructor is executed only once. In other words, once the internal state of a promise changes and a value is obtained, each subsequent call to .then or .catch will directly get the value.

Part 6:

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(console.error)

// [TypeError: Chaining cycle detected for promise #<Promise>]
// Uncaught SyntaxError: Identifier 'promise' has already been declared
//    at <anonymous>:1:1
// (anonymous) @ VM218:1

The value returned by .then or .catch cannot be the promise itself, otherwise, it will cause an infinite loop.

Part 7:

Promise.resolve()
  .then(() => {
    return new Error('error');
  })
  .then(res => {
    console.log('then: ', res);
  })
  .catch(err => {
    console.log('catch: ', err);
  });

// then: Error: error!
// at Promise.resolve.then (...)
// at ...

Returning an error object in .then or .catch does not throw an error, so it will not be caught by subsequent .catch, you need to change to one of them:

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

Because returning any non-promise value will be wrapped into a promise object, that is, return new Error ('error') is equivalent to return Promise.resolve (new Error ('error')).

Part 8:

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

  // 1

The argument of .then or .catch is expected to be a function, and passing in a non-function will results the result of value to be ignored such as .then(2) or .then(Promise.resolve(3).

Part 9:

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

//   fail2:  Error: Error after success
//     at success (<anonymous>:4:13)

.then can accept two parameters, the first is a function that handles success, and the second is a function that handles errors. .catch is a convenient way to write the second parameter of .then, but there is one thing to pay attention to in usage: .then the second error-handling function cannot catch the error thrown by the first successful function and subsequent ones. catch catches previous errors. Of course, the following code works if you want to rewrite:

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

Part 10:

process.nextTick(() => {
  console.log('1')
})
Promise.resolve()
  .then(() => {
    console.log('2')
  })
setImmediate(() => {
  console.log('3')
})
console.log('4');

// Print 4
// Print 1
// Print 2
// Print 3

Both process.nextTick and promise.then belong to microtask, while setImmediate belongs to macrotask, which is executed during the check phase of the event loop. A microtask is executed between each phase of the event loop (macrotask), and the beginning of the event loop is executed once.