Detailed explanation of several solutions for JavaScript interruption requests

Detailed explanation of several solutions for JavaScript interruption requests

1 Promise

One disadvantage of Promise is that once created, it cannot be cancelled, so essentially Promise cannot be terminated.

But we can simulate the interruption of the request by interrupting the call chain or interrupting the Promise.

Interrupt the call chain

Interrupting the call chain means that after a then/catch is executed, subsequent chain calls (including then, catch, and finally) will no longer continue to execute.

The method is to return a new Promise instance in then/catch and keep it in pending state:

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('result');
    });
}).then(res => {
    // When a certain condition is met, return a pending Promise instance to interrupt the call chain if (res === 'result') {
        return new Promise(() => {});
    }
    console.log(res); // no printing }).then(() => {
    console.log('then is not executed'); // no printing}).catch(() => {
    console.log('catch not executed'); // no printing}).finally(() => {
    console.log('finally not executed'); // no printing});

Interrupting a Promise

Interrupting a Promise is not the same as aborting a Promise, because a Promise cannot be terminated.

The interruption here means to reject the pending promise at the right time. For example, a common application scenario is to set a timeout for the network request and interrupt it once the timeout occurs.

As usual, use setTimeout to simulate network requests. The threshold is set to Math.random() * 3000, which means that the result will be returned within 3 seconds.

const request = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Received server data')
  }, Math.random() * 3000)
})

Assuming that more than 2 seconds means network timeout, we can encapsulate a timeout processing function.

Since the events required for network requests are random, the Promise.race method can be used to achieve the purpose of timeout rejection.

const timeoutReject = (p1, timeout = 2000) => {
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('Network timeout');
        }, timeout);
    });
    return Promise.race([p1, p2]);
};

timeoutReject(request).then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

Wrapping abort method - imitating Axios's CancelToken

The above implementation is not flexible, because there are many ways to interrupt Promise, not just network timeout.

We can imitate the core source code of CancelToken in Axios and simply package an abort method for users to call at any time.

function abortWrapper(p1) {
    let abort;
    const p2 = new Promise((resolve, reject) => {
        abort = reject;
    });
    // If there is no resolve or reject, the status of p2 is always pending
    const p = Promise.race([p1, p2]);
    p.abort = abort;
    return p;
}

const req = abortWrapper(request);
req.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

setTimeout(() => {
    // Manually call req.abort to change the status of p2 to rejected
    req.abort('manual abort request');
}, 2000);

The main purpose of such encapsulation is to be able to control its resolve or reject outside the Promise, so that users can manually call resolve (trigger .then) or reject (trigger .catch) at any time.

It should be noted that although the Promise request is interrupted, the promise is not terminated, and the network request may still return, but at that time we no longer care about the request result.

2 RXJS unsubscribe method

Rxjs itself provides a method to unsubscribe.

let stream1$ = new Observable(observer => {
    let timeout = setTimeout(() => {
        observer.next('observable timeout');
    }, 2000);

    return () => {
        clearTimeout(timeout);
    }
});
let disposable = stream1$.subscribe(value => console.log(value));
setTimeout(() => {
    disposable.unsubscribe();
}, 1000);

3 Axios CancelToken

Axios's CancelToken can be used in two ways:

  • Method 1
import axios from 'axios';
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  } 
});

source.cancel('Operation canceled by the user.');
  • Method 2
import axios from 'axios';
const CancelToken = axios.CancelToken;

// Create a variable such as cancel to store the method of interrupting a request let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c; // Assign parameter c to cancel
  })
});

// Determine whether cancel is a function and make sure axios has instantiated a CancelToken
if (typeof cancel === 'function') {
    cancel();
    cancel = null;
}

CancelToken core source code: (axios/lib/cancel/CancelToken.js)

'use strict';

var Cancel = require('./Cancel');

/**
 * A `CancelToken` is an object that can be used to request cancellation of an operation.
 *
 * @class
 * @param {Function} executor The executor function.
 */
function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

/**
 * Throws a `Cancel` if cancellation has been requested.
 */
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throw this.reason;
  }
};

/**
 * Returns an object that contains a new `CancelToken` and a function that, when called,
 * cancels the `CancelToken`.
 */
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

module.exports = CancelToken;

It can be seen that at the bottom of Axios, the idea embodied in the core source code of CancelToken is consistent with the idea of ​​interrupting the Promise packaging abort method above.

It's just that Axios manually calls resolve externally (the user triggers the cancel method), and once resolve is called, it triggers the then method of promise. Let's look at the source code of promise.then: (axios/lib/adapters/xhr.js)

if (config.cancelToken) {
  // Handle cancellation
  config.cancelToken.promise.then(function onCanceled(cancel) {
    if (!request) {
      return;
    }

    request.abort();
    reject(cancel);
    // Clean up request
    request = null;
  });
}

You can see that the abort method is executed in the then method to cancel the request, and reject is called to make the outer promise fail.

This is the end of this article about several detailed solutions for JavaScript interrupt requests. For more relevant js interrupt request content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • JavaScript code library cookieLibrary.js that writes cookies
  • Building a JS code base from the ground up
  • Simple usage of java FastJson
  • Vue.js performance optimization N tips (worth collecting)
  • AngularJS implements the sample code of expanding and shrinking some columns of the table
  • JavaScript implements the most complete code analysis of a simple magnifying glass (ES5)
  • JavaScript to implement the most complete code analysis of simple carousel (ES6 object-oriented)
  • JavaScript to implement simple carousel chart most complete code analysis (ES5)
  • JavaScript to implement the most complete code analysis of a simple shopping cart (ES6 object-oriented)
  • 5 ways to make your JavaScript codebase cleaner

<<:  Introduction to Docker Architecture

>>:  Detailed example of using the distinct method in MySQL

Recommend

MySQL infobright installation steps

Table of contents 1. Use the "rpm -ivh insta...

The difference and choice between datetime and timestamp in MySQL

Table of contents 1 Difference 1.1 Space Occupanc...

How to use history redirection in React Router

In react-router, the jump in the component can be...

Do you know the difference between empty value and null value in mysql

Preface Recently I found that my friend's met...

Syntax alias problem based on delete in mysql

Table of contents MySQL delete syntax alias probl...

How to solve the problem that the project in eclipse cannot be added to tomcat

1. Right-click the project and select properties ...

Turn off the AutoComplete function in the input box

Now we can use an attribute of input called autoco...

Implementing custom scroll bar with native js

This article example shares the specific code of ...

The difference between MySQL database host 127.0.0.1 and localhost

Many of my friends may encounter a problem and do...

Definition and function of zoom:1 attribute in CSS

Today I was asked what the zoom attribute in CSS ...

Write a mysql data backup script using shell

Ideas It's actually very simple Write a shell...

The neglected special effects of META tags (page transition effects)

Using js in web design can achieve many page effec...

foreman ubuntu16 quick installation

Quickstart Guide The Foreman installer is a colle...

JS cross-domain XML--with AS URLLoader

Recently, I received a requirement for function ex...