index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import ReduxThunk from 'redux-thunk';
import reducer from './redux';
const body = document.querySelector('body'),
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose,
store = createStore(reducer, composeEnhancers(applyMiddleware(ReduxThunk)));
ReactDOM.render(<Provider store={store}><App/></Provider>, body);
App.js
import React from 'react';
import Shortcut from './Shortcut';
export default class App extends React.PureComponent {
render() {
return <Shortcut/>;
}
}
Shortcut.js
import React from 'react';
import { connect } from 'react-redux';
import { print_date_async } from './redux';
class Shortcut extends React.PureComponent {
componentDidMount() {
window.addEventListener('keydown', (event) => {
if (event.keyCode === 13) {
this.props.print_date_async({ date: new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1") });
}
});
}
render () {
return null;
}
}
function mapDispatchToProps(dispatch) {
return {
print_date_async: (date) => dispatch(print_date_async(date))
};
}
Shortcut = connect(undefined, mapDispatchToProps)(Shortcut);
export default Shortcut;
redux.js
import { createAction, handleActions } from 'redux-actions';
export const print_date = createAction('print_date');
export function print_date_async (payload) {
return async (dispatch) => {
try {
await wait_async();
dispatch(print_date({ date:payload.date }));
}
catch (exeption) {
console.error(exeption);
}
};
}
const initial_state = { };
export default handleActions({
print_date: (state, action) => {
console.log(action.payload.date);
return { ...state }
}
}, initial_state);
function wait_async (number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, Math.floor(Math.random() * 10000)); // 0000 ~ 9999
});
};
https://codesandbox.io/s/l7y4rn61k9
为了解释我创建的程序作为一个例子,当你按下回车键时,按下回车的时间会在几秒钟之后输出。
我希望在调用一个异步动作创建者之后调用下一个动作创建者。
如果按住Enter键,第一次按下的结果也可以稍后打印。
01:42:48
01:42:48
01:42:47
01:42:47
01:42:47
01:42:47
01:42:48
我考虑导出变量以检查状态,但我不喜欢它。我也不喜欢检查按键之间的间隔。
我想通过以下方式实现,但实施起来并不容易。如果您对此有所了解,请回答。谢谢你的阅读!
window.addEventListener('keydown', (event) => {
if (event.keyCode === 13) {
if (!this.proceeding) {
this.proceeding = true;
(async () => {
await this.props.print_date_async({ date: new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1") });
this.proceeding = false;
})();
}
}
});
我只需要在使用redux-thunk时返回promise对象。
export function print_date_async (payload) {
return (dispatch) => new Promise(async (resolve, reject) => {
try {
await wait_async();
dispatch(print_date({ date:payload.date }));
resolve();
}
catch (exeption) {
console.error(exeption);
reject(exeption);
}
});
}
我知道async()会返回promise对象,所以你不必用promise包装。
答案 0 :(得分:1)
问题是您有一个用户输入流,可以创建异步值流。这些值可以按照与用户输入顺序不同的顺序解析。
例如:用户点击A,B和C这会创建Promise A,B和C,但它们按照C,B和A的顺序解析。您希望它们按照与用户操作相同的顺序解析吗?< / p>
使用promises解析对用户生成的流进行排序或加入非常复杂,也许Reactive x可以解决这个问题,但以下代码应该这样做:
const resolveOrderedAndFast = (function(promiseInfos,requestIndex){
const lastCall = {};
return fastHandler => orderedHandler => promise => {
requestIndex++;
promise.then(fastHandler);
promiseInfos.push([promise,orderedHandler,requestIndex]);
//only resolve when it's last call
const last = () => {
lastCall.id={};
var check = lastCall.id;
return promise.then(
resolve=>
Promise.all(
promiseInfos
.sort((x,y)=>x[2]-y[2])
.map(([p,fn,requestIndex])=>{
return p.then(
([r,seconds])=>{
return [r,seconds,fn,requestIndex];
}
)
})
).then(
(resolves)=>{
if(check===lastCall.id){
resolves.forEach(
([r,seconds,fn,requestIndex])=>
fn([r,seconds,requestIndex])
);
promiseInfos=[];
requestIndex=0;
}else{
//ignore, no problem
}
}
)
);
};
last();//only resolve the last call to this function
};
}([],0))
const later = resolveValue => {
const values = ["A","B","C"];
const index = values.indexOf(resolveValue);
return new Promise(
(resolve,reject)=>
setTimeout(
x=>resolve([resolveValue,(4-(index*2))])
,(4-(index*2))*1000
)
)
};
const fastHandler = val => ([resolve,seconds])=>
console.log(
val,
"FAST HANDLER --- resolved with:",
resolve,
"in",seconds,"seconds"
);
const orderedHandler = val => ([resolve,seconds,i])=>
console.log(
"Call id:",
i,
"ORDRED HANDLER --- resolved with:",
resolve,
"in",seconds,"seconds"
);
const valueArray = ["A","B","C","A","B","C","A"];
console.log("making request:",valueArray);
valueArray
.forEach(
val=>
resolveOrderedAndFast
(fastHandler(val))
(orderedHandler(val))
(later(val))
);
setTimeout(
()=>
console.log("making more requests:",valueArray) ||
valueArray
.forEach(
val=>
resolveOrderedAndFast
(fastHandler(val))
(orderedHandler(val))
(later(val))
)
,500
);
这是一个更简单的版本,对它的作用有更多的评论:
const Fail = function(reason){this.reason=reason;};
const resolveOrderedAndFast = (function(preIndex,postIndex,stored){
return fastHandler => orderedHandler => promise => {
//call this function before the promise resolves
const pre = p=> {
preIndex++;
p.then(fastHandler);
(function(preIndex,handler){//set up post when promise resolved
//add pre index, this will be 1,2,3,4
// but can resolve as 2,4,3,1
p.then(r=>post([r,handler,preIndex]))
//because the promises are stored here you may want to catch it
// you can then resolve with special Fail value and have orderedHandler
// deal with it
.catch(e=>
post(
[
new Fail([e,r,preIndex]),//resolve to Fail value if rejected
handler,
preIndex
]
)
)
})(preIndex,orderedHandler);//closure on index and ordered handler
};
//this will handle promise resolving
const post = resolve=>{
//got another resolved promise
postIndex++;
//store the details (it's resolve value, handler and index)
stored.push(resolve);
//deconstruct the resole value
const [r,handler,i]=resolve;
//the index of the resolve is same as current index
// that means we can start calling the resolvers
if(i===postIndex){
//sort the stored by their indexes (when they stared)
stored = stored
.sort((x,y)=>x[2]-y[2])
//filter out all the ones that can be resolved
.filter(
([r,handler,i])=>{
//can be resolved, no promises have yet to be resolved
// before this index
if(i<=postIndex){
//increase the number of indexes procssed (or stored)
postIndex++;
//resolve the value again (so we don't get errors)
Promise.resolve([r,i]).then(handler);
return false;//no need to "resolve" this again, filter it out
}
return true;
}
)
}
};
pre(promise);
};
})(0,0,[])//passing in pre and post index and stored
//demo on how to use it:
const later = resolveValue => {
const values = ["A","B","C"];
const index = values.indexOf(resolveValue);
return new Promise(
(resolve,reject)=>
setTimeout(
x=>resolve([resolveValue,(4-(index*2))])
,(4-(index*2))*1000
)
)
};
const fastHandler = val => ([resolve,seconds])=>
console.log(
val,
"FAST HANDLER --- resolved with:",
resolve,
"in",seconds,"seconds"
);
const orderedHandler = val => ([[resolve,seconds],i])=>
console.log(
"Call id:",
i,
"ORDRED HANDLER --- resolved with:",
resolve,
"in",seconds,"seconds"
);
const valueArray = ["A","B","C","A","B","C","A"];
console.log("making request:",valueArray);
valueArray
.forEach(
val=>
resolveOrderedAndFast
(fastHandler(val))
(orderedHandler(val))
(later(val))
);
setTimeout(
()=>
console.log("making more requests:",valueArray) ||
valueArray
.forEach(
val=>
resolveOrderedAndFast
(fastHandler(val))
(orderedHandler(val))
(later(val))
)
,500
);