Realize an "interruptible" ajax

Recently the use of Ajax, API found in open()the third parameter (whether synchronous execution) false values is disabled?
You can see in the console, although the request can be sent normally:
ajax_console

But does not normally execute onreadystatechangecallback function statement:
async_ajax

This also means: Ajax can only support asynchronous mode!

This is of course a good thing: because sending a synchronous request will cause the browser to enter a temporary suspended animation state, especially for requests that require processing of large amounts of data and long-waiting interfaces. This leads to a "failed" user experience.


The interviewer asked me a question when I was contacted by Amoy Department two days ago: If you need to send multiple requests to obtain different data in a project you have done, and these requests need to be processed differently based on the last returned data, you will How to do it?
That necessarily consider promisecreating asynchronous http requests environment ah! like this:

let readUrlPromise=url=>{
    
    
    return new Promise((resolve,reject)=>{
    
    
        let xhr=new XMLHttpRequest();
        xhr.open("GET",url);
        xhr.onreadystatechange=function(){
    
    
            if(xhr.readyState==4 && xhr.status==200){
    
    
                // console.log(JSON.parse(xhr.responseText));
                // reject("请求失败了");
                resolve(JSON.parse(xhr.responseText));
            }else if(xhr.readyState==4 && xhr.status!=200){
    
    
                reject('请求失败');
            }
        }
        xhr.onerror=function(){
    
    
            reject('请求失败');
        }
        xhr.send(null);
    })
}
readUrlPromise("这里是百度地图某url,就不放出来,嘿嘿").then(data=>{
    
    
	console.log('1231');
	console.log(data);
	return readUrlPromise("这里是百度地图某url,就不放出来,嘿嘿")
}).then(data2=>{
    
    
	console.log('2231');
	console.log(data2)
})

In fact, what I’m talking about here is: if the first request does not return data or an error occurs, then the subsequent requests are actually unnecessary-otherwise a loading turns in the middle of the page for a long time and then the user looks anxiously looking forward to it. There is a big Error , which is a bit uncomfortable.

But then I considered another question: if these two (or more) requests (or these requests to other requests) can affect each other, for example: if the current request returns abnormal data or carry data on the user center page If a certain flag bit or the user triggers another operation, the request that has already started execution is cancelled.
This can also be used in scenarios such as "button accidentally touched".

When I thought of this, I suddenly thought of a ready-made library-axios!
Many people may not have used it. But in fact, the Axios request interceptor not only be used to process, processing data, can also block the transmission of this request: thanks interceptor mechanism Axios

// 请求拦截器
axios.interceptors.request.use(config=>{
    
    
	// ...
},error=>{
    
    
	return Promise.reject(error)
})

// 响应拦截器
axios.interceptors.response.use(response=>{
    
    
	// ...
},error=>{
    
    
	return Promise.reject(error)
})

Inside, axios added a named CancelTokenproperty, built for the completion of the "Cancel Request" operation, said the document which can be used for multi-point anti inadvertently , cancel token:
In order to achieve this goal, we can set the global "Get the cancellation request class":

const cancelToken=axios.CancelToken;

Then use the factory class to create a cancellation object in the axios request or in the request interceptor and set the cancellation token in the header information:

const source=cancelToken.source();
config.cancelToken=source.token;

Finally, the request interceptor determines example, if the current state is loading or has sent a request to a (for example provided with a string comparison or determination flag), triggered cancel () method to cancel:

source.cancel('可能误触,已拦截当前请求');

Going back to the above statement, learning from axios-token, we may be able to achieve a " cancellable promise ".

Everyone knows that once the logic of a promise is executed, it cannot be stopped unless it is executed.

let ps=new Promise((resolve,reject)=>{
    
    
	resolve('这是result');
	// 注意这个resolve
	resolve('我才是真正的result,但是被忽略了...')
	console.log('没有感情的runing...')
})
console.log(ps)

World famous paintings-robbed promise

As above, we can actually encapsulate the promise in one layer, exposing a cancellation function to the outside, and call this function when we need to cancel the promise. But we really just put a non-asynchronous promise resolve or even reject method in the function, so that when the interface (in the original request) gets a response, the resolve or reject (program) will be ignored.
In this way, we can "simulate" blocking the promise!

Let's first look at the effect-here is a certain interface of the requested Baidu map. In order to achieve the effect of "some large data request delay", the author here uses setTimeoutwait 2s:
cancel-promise

<div class="page">
    <div class="article">
        <h1>我是标题部分</h1>
        <p>黑化肥发灰,灰化肥发黑</p>
        <p>黑化肥发灰会挥发;灰化肥挥发会发黑</p>
        <p>黑化肥挥发发灰会花飞;灰化肥挥发发黑会飞花</p>
    </div>
    <p class="footer">嘿嘿</p>

    <button class="send">发送请求</button>
    <button class="cancel">取消</button>
</div>
const baseURL='https://devapi.qweather.com/v7/weather/24h?location=这里是纬经度坐标&key=这里是百度地图key值';

// 初始化取消函数,防止调用时报错
let cancelFn=function(){
    
    }

function request(req){
    
    
	return new Promise((resolve,reject)=>{
    
    
		let xhr=new XMLHttpRequest();
		xhr.open(req.method || 'GET',baseURL);
		xhr.onload=function(){
    
    
			if(xhr.readyState==4 && (xhr.status>=200 && xhr.status<300)){
    
    
				setTimeout(()=>{
    
    
					resolve({
    
    data:JSON.parse(xhr.responseText)})
				},2000)
			}else{
    
    
				reject(xhr.status)
			}
		}
		xhr.onerror=function(){
    
    
			reject('请求失败了...')
		}
		xhr.send(req.data || null);

		// 向外暴露取消函数
		cancelFn=function(msg){
    
    
			reject({
    
    message:msg})
		}
	})
};

// test
// 上面那些完全可以放在单独的文件中被外链
let send=document.querySelector('.send');
let cancel=document.querySelector('.cancel');
send.addEventListener('click',async function(){
    
    
	console.log('请求进行中...')
	let {
    
    data}=await request({
    
    })
	console.log(data)
});
cancel.addEventListener('click',function(){
    
    
	cancelFn('取消了上一次的请求');
})

So far the effect has been achieved, but there are three points that "have to be mentioned" in the ajax request:

  1. xhr.onerror: onreadystatechangeEvent is for each request status ( xhr.readyState) when the changes will trigger a request to change the trigger several times event, so do not in on readyStateand statusjudgment with elseexposure reject, as big as the probability of your program will not execute properly!
  2. xhr.onload: I found a very strange thing, if your ajax wrapped in promise and in the implementation of their own, then you can use the onreadystatechangeevent callbacks to do, but if it is to be called in the event triggered by the user, then in which can only be used onloadto make a callback, the specific reasons emmmmmmm inform welcome!
  3. In the above code, the cancelFn function is initialized externally (globally). The first is to "trigger the cancel function when the request function is not triggered, and it will not report an error but no response"; the second is to "simulate an externally exposed function operation in the request function." "

Of course, there are similar and more convenient http request libraries and fetch and the like. As for whether to use a native or a packaged library, which method to use in which project depends on how you choose...


Related extensions

The above mentioned, whether it is axios interceptorsor the encapsulation of promise + ajax, all "nakedly" mentioned a concept: interception!
And in the more famous mini-proxy proxy module in node, I also found the relevant code:

function MiniProxy(options){
    
    
	//...
	this.onBeforeRequest=options.onBeforeRequest || function(){
    
    };
	this.onBeforeResponse=options.onBeforeResponse || function(){
    
    };
	//...
}

Guess you like

Origin blog.csdn.net/qq_43624878/article/details/114848668