Fs checkasyncrequest returned error for model

Help! Async request failed !!! [решено]

При подключении к серверу Альт Линукс Школьный по WEB (https://myserver:8080)
после ввода пароля root выдается вот такая запись:

Async request failed
Request status: error
Response:
                                 OK

и все… Хотя вот буквально неделю назад спокойно по ВЭБу коннектился
С консоли спокойно пускает а с ВЭБа не хочет… Подскажите «ламеру» — где рыть?

« Последнее редактирование: 18.11.2010 00:21:56 от Skull »


Записан


service alteratord resart
service ahttpd restart
в консоли сервера. Или перегрузиться.


Записан

Андрей Черепанов (cas@)


перегружаться пробовал, не помогает…


Записан



Записан

Андрей Черепанов (cas@)


нет, т.к. в автоматическом режиме после произведенных обновлений почти весь ВЭБ интерфейс стал на английском, а у меня есть люди, которые в нем не сильны… поэтому пришлось снести весь сервер, поставить начисто заново и не производить обновления…


Записан


Наверное надо было что-то доставить, или откорректировать, что-бы было по-русски.
Т.е. сервер поставили заново, а клиенты настроены на старый сервер?


Записан


Да клиенты, собссно, толком и не заводились, пока шли и идут эксперименты, в т.ч. и с обновлениями!
Но вот с обновлениями что-то не так пошло.
Поэтому и было принято решение снова все поставить начисто, не обновляться, и уже с чистого листа начать…
Но теперь новый глюк — не пущает по ВЭБ-морде…


Записан


Наверное надо было что-то доставить, или откорректировать, что-бы было по-русски.

А разве это не готовое решение, обновление которого не требует дополнительных настроек?
Я почему то считал, что обновления не требуют доп.настроек


Записан


Можно сказать, как правило, не требует, но бывают исключения по различным причинам.


Записан


мдя…еще бы знать эти причины


Записан


Вопчем проблема решилась… С ВЭБ интерфейса вхожу…
Обратил внимание на то, что резервными копиями был забит весь диск /var/srv
причем последняя копия даже не сформировалась, о чем внятно было написано в логах
снес с консоли все резервные копии, перезапустил сервер и зашел с ВЭБ-интерфейса
Всем спасибо за участие!


Записан


New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.

Already on GitHub?
Sign in
to your account


Closed

msutkowski opened this issue

Feb 24, 2020

· 17 comments

Comments

@msutkowski

With the current implementation of createAsyncThunk, there is no way to access any errors returned from a server. Here is a specific scenario which I imagine is extremely common:

You attempt a form submission with a set of data like POST /users with a body of { first_name: 'valid', last_name: 'valid', email: 'in@valid' } and the API responds with a 400:

{
    "details": [
        {
            "message": ""email" must be a valid email",
            "path": [
                "email"
            ],
            "type": "string.email",
            "context": {
                "value": "in@valid",
                "key": "email",
                "label": "email"
            }
        }
    ],
    "source": "payload",
    "meta": {
        "id": "1582509238404:1c50114e-ef8e-4d97-b3e4-dbc995e8fcff:20:k6zpbe70:10030",
        "api": "1.0.0",
        "env": "production"
    }
}

In this case, I need some way to access that error so that I could update my UI — for example, by unwrapping the details field and using Formik’s setErrors to provide feedback. I imagine it’d also be valuable for detailed toasts as well, where people useEffect and pop an alert if the error message changes.

After looking through createAsyncThunk, the only real solution I can think of is

  • Passing a new parameter for rethrow and rethrowing the error instead of returning finalAction.

If we were able to rethrow, we could do this sort of thing:

<Form 
   onSubmit={async (values, helpers) => {
          try {
            await dispatch(myAsyncThunk(values));
          } catch (err) {
            const joiErrors = err.response.details;
            if (joiErrors) {
              helpers.setErrors(unwrapErrors(joiErrors));
            }
          }
        }}
      />

@markerikson

Per DMs, my inclination at this point is that if you need to reformat anything from the server, it’s your job to do it in the payload creator.

Axios requests where you need to grab something out of response.data, normalization of entity data, extracting important values from a server error… we don’t know anything about that ourselves. Do it yourself in the payload creator, and whatever you return or throw will end up in the corresponding action.

@markerikson

Hmm. After further conversation, I’m still not convinced that «rethrowing» is the right idea. However, if the payload creator extracts some kind of an object from the error and tries to rethrow that, miniSerializeError() will assume it’s actually an Error due to typeof error === 'object' and tried to read out the standard fields, vs just passing through the thrown object to the rejected action. That seems insufficient.

Can we do an instanceof Error check here?

@phryneas

Hmm…

This makes me question two things:

  • Do you need Redux at all here?

If you go to the lengths to even do exception handling from your component — are you even using the global store here? Does Redux make your life more easy here, or is it just a burden? Do you maybe even throw away the submission result (beyond the errors), so nothing of meaning would ever be stored in your redux store?

  • Is this an error from the programmer’s view?

Sure, for the user it’s an error. But for you as a programmer, that seems more like a result that you are maybe even expecting on more than 50% the responses. Is this still an error? Or wouldn’t it make more sense to reserve the error/rejected way for things like connection timeouts etc. and handling this as a part of the «fulfilled» route?
I mean, the fetch was a success, it’s just not the result the user wants to see, but for your app it’s a pretty normal state, not really an «exception».
So you could just return this from the payloadCreator.

@markerikson

Can we do an instanceof Error check here?

I’d rather not go down this road. In TypeScript, just extending Error will give you a class that does not really extend error.
You’ve got to do extra magic to get a class that extends Error from the engine viewpoint.
So this is a very unreliable check for anything.

We could allow the payloadCreator to return/throw own fulfilled/rejected actions (check isFSA and actionCreator.fulfilled.match/reject on the return value/error) and let those override our finalAction. For those, we could just assume that they are serializable.

We could even think about bailing from miniSerializeError from conditions like err.constructor === Object or !(/(error|exception)/i.test(err.constructor.name)) — as this will probably be developer-provided data. If that isn’t serializable, the serializability check will warn the developer in dev mode about it.

@msutkowski

@phryneas Thanks for the insight!

Do you need Redux at all here?
In this instance, let’s pretend we’re doing PUT /users on form submit that either returns a new user instance { user: { id: 1, name: test, ... } or an error with some validation issues that come back in a 400 with a detailed message as shown. Typically, this would be a thunk as you’re going to have a reducer handle inserting the returned user upon success.

Do you need redux at this point?

No, not really — we could dispatch an addUser() action after the success, or a setErrorMessage on the failure in the event we — but IMO this is more tedious and eliminates the point of a createAsyncThunk abstraction.

Is this an error from the programmer’s view?

I might not be understanding correctly, but if you returned the error in the payloadCreator after catching it, you’d then be doing things like this, which seems like a more confusing approach?

[updateUserThunk.fulfilled]: (state, { payload }) => {
 // we would need to force a check here, always, as we could be returning "an error"
if (payload.user) {
  state.users = upsert(payload.user)
}

My other thought matches up with yours and I’d be happy to open a PR with that as well:

We could even think about bailing from miniSerializeError from conditions like err.constructor === Object or !(/(error|exception)/i.test(err.constructor.name)) — as this will probably be developer-provided data. If that isn’t serializable, the serializability check will warn the developer in dev mode about it.

I think this is probably the best approach. My only additional feedback there is we might want to consider forcing a standardized error format when throwing a custom error object — the current format looks good, but we’d want to add a key such as data. In my example, that would look like this:

export const axiosUpdateThunk = createAsyncThunk(
  "users/update",
  async ({ data }) => {
    try {
      const result = await axios.put(`api/users`, data);
      return result.data;
    } catch (err) {
      const customError = {
        name: "Custom axios error",
        message: err.response.statusText,
        data: err.response.data // serializable
      };
      throw customError;
    }
  }
);

The benefit of this being an established pattern that offers predictable handling inside of all reducers — [whatever.rejected]: (state, action) => { state.error = action.payload.error } would always have the given shape. I could also see the counterpoint where this library shouldn’t care about if a developer wants to dump an error with no context into the error message and we just serialize it away — but I think allowing that to happen in the data field is a good compromise.

@phryneas

@phryneas Thanks for the insight!

Do you need Redux at all here?
In this instance, let’s pretend we’re doing PUT /users on form submit that either returns a new user instance { user: { id: 1, name: test, ... } or an error with some validation issues that come back in a 400 with a detailed message as shown. Typically, this would be a thunk as you’re going to have a reducer handle inserting the returned user upon success.

Do you need redux at this point?

No, not really — we could dispatch an addUser() action after the success, or a setErrorMessage on the failure in the event we — but IMO this is more tedious and eliminates the point of a createAsyncThunk abstraction.

If you actually use those actions dispatched from createAsyncThunk, that’s totally fine.
It just seemed to me like you were using redux here, to in the end just derive a local component state and possibly not even modify the store in response. In which case you had no use-case for redux at all, it would just complicate your life.
Personally, I’d try to keep the logic either completely in redux or in the component, doing half of the logic in one and the other one in the other defeats the purpose a little bit for me.

Is this an error from the programmer’s view?

I might not be understanding correctly, but if you returned the error in the payloadCreator after catching it, you’d then be doing things like this, which seems like a more confusing approach?

[updateUserThunk.fulfilled]: (state, { payload }) => {
 // we would need to force a check here, always, as we could be returning "an error"
if (payload.user) {
  state.users = upsert(payload.user)
}

You’re gonna have that distinction either in the fulfilled reducer, or in the rejected reducer, as besides «server returned code 4/5xx», there are other possible reasons for a rejection you’d have to test for somewhere.
Depending on what you do in the end, it might fit in better with the one or the other. I just wanted to bring up the question, as it seems that if you moved this into the resolve branch, it should already be working for you.
(For example, fetch sees it like this: any kind of response, independent of status code, is a «successful» response as in «planned communication between server and client» and it throws errors only on actual network or CORS errors. I actually like that mindset.)

@msutkowski

Gotcha. I’m a fan of throwing custom error messages as shown, as it plays nicely with both selecting well-defined errors from the store, as well as having the option of unwrapResulting the actual desired detailed error data for consumption directly in the component. I was operating under the assumption that rejected would be replacing the common use case of NETWORK_REQUEST_FAILURE type of actions, where most implementations I’ve seen seem to catch & throw fetch errors to match this pattern.

As a side note, if we were to go with that mindset of matching the fetch behavior, we’d need to catch axios (and i’m assuming quite a few other fetching libraries) errors, then return them to handle them in fulfilled… which seems like it might cause some friction.

@phryneas

You do what works best for you there, no question!
I just wanted to give an explanation for my reasoning above :)

And yes, there seems to be a disconnect between fetch and other libraries — I don’t really think one is better than the other, as both require you to do very specific things.

Completly new train of thought:

What is a bit of a shame here is that we cannot really provide any type information for the error of the rejected event, as it can literally be everything thrown anywhere — and TS doesn’t enforce types for throw. (And neither try ... catch nor promise.catch(...) have any way of guaranteeing a specific type.)
So I haven’t really seen the rejected path as something I would want to pass a lot of data down where I’d probably rely on the structure of that data.

If we would think my suggestion from above further:

We could allow the payloadCreator to return/throw own fulfilled/rejected actions (check isFSA and actionCreator.fulfilled.match/reject on the return value/error) and let those override our finalAction. For those, we could just assume that they are serializable.

In that case, we could allow devs to specify a generic type for rejected.payload and that way, data could be passed out of error situations by returning a rejected action with a set payload.

This might be the most TS-compatible way of handling this. If you still want to unwrapResult to do a catch, you can still do that — but you’ll use type information that way. (As, again, a catch never has a type.)

@msutkowski

I think that makes a lot of sense. The typing on that would be nice as we’d just be able to do something like RejectedPayload = AxiosError<MyKnownResponse>

I did some experimenting with all of the ideas that are outlined here, and from a usability standpoint, handling success/’errors’ in fulfilled isn’t that awkward at all in its current state, but your idea would make it a straight forward.

@markerikson

So what’s the consensus out of that discussion, if any?

@msutkowski

I was experimenting with a lot of different ways to implement the types on this earlier and didn’t come up with anything notable. I’m going to keep at it and see if I can get something to pan out.

@msutkowski

This was addressed in #393

@infostreams

Ok, just for future Googler’s who can’t seem to figure out how to access the original response body. I’ve created a helper api class (as follows)

const handleRequest = (request, thunkApi) =>
    request
            .then(response => response.data)
            .catch(error => thunkApi.rejectWithValue(error?.response?.data || error))

const buildUrl = (url, params) => {
    // Support URLs with named identifiers, such as '/posts/get/:id'.
    // This code replaces the ':id' part with the value of params.id
    Object.keys(params).forEach(k => {
        if (url.indexOf(':' + k) > -1) {
            url = url.replace(':' + k, params[k])
            delete params[k]
        }
    })

    // all the parameters that were not bound to a named identifier are appended to the URL
    const encoded = Object.entries(params).map(kv => kv.map(encodeURIComponent).join("=")).join("&")
    return url + (encoded.length > 0 ? '?' + encoded : '')
}

export default class api {
    static post = (url, axiosConfig = {}) => (obj = {}, thunkApi) =>
        handleRequest(axios.post(url, obj, axiosConfig), thunkApi)

    static get = (url, axiosConfig = {}) => (obj = {}, thunkApi) =>
        handleRequest(axios.get(buildUrl(url, obj), axiosConfig), thunkApi)

    static delete = (url, axiosConfig = {}) => (obj = {}, thunkApi) =>
        handleRequest(axios.delete(buildUrl(url, obj), axiosConfig), thunkApi)
}

just so that I can easily create async thunks like this

import {createAsyncThunk} from '@reduxjs/toolkit'
import api from "service/api"
export const requestPasswordReset = createAsyncThunk('login/requestReset', api.post('/password/email'))

and refer to the originally returned JSON in my extraReducers as follows

        [requestPasswordReset.rejected]: (state, action, more) => {
            state.loading = 'error'
            state.error = action?.payload
        },

Now, state.error will contain the JSON that was returned by my API. Hope this helps someone.

amir5121, lqqokim, sebastiandelaroche, martin8877, kzviagina, noahfpf, lefant, jmuela1015, tpnovaldev, and muhammetakkus reacted with thumbs up emoji
aneeq-ur-rehman4 reacted with thumbs down emoji
lqqokim and jmuela1015 reacted with laugh emoji
lqqokim and jmuela1015 reacted with hooray emoji
orszaczky, lqqokim, medi11, and jmuela1015 reacted with confused emoji
lqqokim and jmuela1015 reacted with heart emoji
lqqokim and jmuela1015 reacted with rocket emoji
lqqokim, quangkhuat1995, and jmuela1015 reacted with eyes emoji

@lqqokim

how pass parameters ??

export const requestPasswordReset = createAsyncThunk('login/requestReset', api.post('/password/email'))

@infostreams

Hi, I updated the code in my comment so that you can now also properly use parameters in GET and DELETE requests. There’s even support for named parameters, so your URL can be something like «/post/get/:id» and it will replace the :id with the appropriate value.

You can pass these values when dispatching the action, for example

this.props.dispatch(requestPasswordReset({username:"someone@example.com"})

@lqqokim

Hi, I updated the code in my comment so that you can now also properly use parameters in GET and DELETE requests. There’s even support for named parameters, so your URL can be something like «/post/get/:id» and it will replace the :id with the appropriate value.

You can pass these values when dispatching the action, for example

this.props.dispatch(requestPasswordReset({username:"someone@example.com"})

Thank you very much for fixing it in a better way.
But I took the following method to make the url change for the axios method and the choice for params, queryString more convenient.

users.js

import {createAsyncThunk} from '@reduxjs/toolkit'
import {http, thunkHandler} from '../utils/api';

export const signInUser = createAsyncThunk(
    'users/signInUser',
    (user, thunkAPI) => thunkHandler(http.post('/users/signIn', user), thunkAPI);
)

api.js

export const http = axios.create({
    baseURL: apiUrl,
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
    },
});

export const thunkHandler = async (asyncFn, thunkAPI) => {
    try {
        const response = await asyncFn;
        return response.data.result;
    } catch (error) {
        return thunkAPI.rejectWithValue(error.response.data);
    }
};

@jmuela1015

Based on the example from @infostreams this is what I came up with … just incase it helps someone else.

UPDATED per the suggestions from @markerikson. Thank you!

import {createAsyncThunk, createSlice} from '@reduxjs/toolkit'
import axios from "axios";

export const signInUser = createAsyncThunk(
  'users/signInUser',
  async (user, thunkApi) => {
    return axios.post('/users/signIn', user)
      .then(response => response.data.results)
      .catch(error => thunkApi.rejectWithValue(error?.response?.data || error))
  }
)

export const usersSlice = createSlice({
  name: 'users',
  initialState:  { user: {}, errors: [] },
  reducers: {},
  extraReducers: builder => {
    builder
      .addCase(signInUser.pending, state => {
        state.errors = []
      })
      .addCase(signInUser.fulfilled, (state, action) => {
        state.errors = [];
        state.user = action.payload;
      })
      .addCase(signInUser.rejected, (state, {payload}) => {
        switch (payload?.response?.status) {
            case 401:
                state.errors.push({ error: "Access denied." }); break;
            case 403:
                state.errors.push({ error: "Forbidden." }); break;
            default:
                state.errors.push(payload); break;
        }
      })
  }
});


export const { } = usersSlice.actions;
export default usersSlice.reducer;

@markerikson

Hmm, this is a tricky one. Even detecting whether the file exists as a pre-flight doesn’t guarantee that you can successfully open it (it could be locked or have permission issues) and detecting if it exists before opening is a classic race condition in server development (small window, but still a race condition).

I’m still thinking there must be a better way to get an error out of a fs.createReadStream(), but the only way I could find was to wrap it in a promise that only resolves when the file is successfully open. That lets you get the error from opening the file and propagate it back to the caller of your async function. Here’s what that would look like:

const fs = require('fs');
const readline = require('readline');

function createReadStreamSafe(filename, options) {
    return new Promise((resolve, reject) => {
        const fileStream = fs.createReadStream(filename, options);
        fileStream.on('error', reject).on('open', () => {
            resolve(filestream);
        });

    });
}

async function processLineByLine(f) {
  const fileStream = await createReadStreamSafe(f);

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine("nofile").catch(err => {
    console.log("caught error");
});

This makes it so that the promise that processLineByLine() returns will reject and you can handle the error there which is what I think you were asking for. If I misunderstood what you were asking for, then please clarify.

FYI, this seems to me to be a bug in readline.createInterface() because it seems like it should reject on the first iteration of for await (const line of rl), but that doesn’t appear to be what happens.

So, as a consequence of that, even this work-around won’t detect read errors on the stream after it’s opened. That really needs to be fixed internal to createInterface(). I agree both a file open error or a read error should show up as a reject on for await (const line of rl).


Another work-around for the file open issue would be to pre-open the file using await fs.promises.open(...) and pass the fd to fs.createReadStream and then you would see the error on the open yourself.


A Different Solution — Wrapping the readLine iterator to add error handling

Warning, this ends up looking like a bit of hack, but it’s a really interesting learning project because I ended up having to wrap an the readline asyncIterator with my own in order to reject when I detected an error on the readStream (the error handling that the readline library is missing).

I set out on a mission to figure out how to write a processLineByLine() function that would return an asyncIterator that would properly reject on stream errors (even though the readline code has bugs in this regard) while still using the readline library internally.

The goal was to be able to write code like this:

for await (let line of processLineByLine("somefile1.txt")) {
     console.log(line);
 }

that properly handles errors on the readStream used internally, whether the file doesn’t exist, exists but can’t be opened or even encounters a read error later while reading. Since I’m not changing/fixing the readline interface code internally, I had to install my own error listener on the readStream and when I see an error there, I need to cause any pending or future promises from the readline interface to reject.

Here’s what I ended up with:

// This is an experiment to wrap the lines asyncIterator with our own iterator
// so we can reject when there's been an error on the readStream.  It's really
// ugly, but does work.

const fs = require('fs');
const readline = require('readline');

function processLineByLine(filename, options = {}) {
    const fileStream = fs.createReadStream(filename, options);
    let latchedError = null;
    let kill = new Set();

    fileStream.on('error', (err) => {
        latchedError = err;
        // any open promises waiting on this stream, need to get rejected now
        for (let fn of kill) {
            fn(err);
        }
    });

    const lines = readline.createInterface({
        input: fileStream,
        crlfDelay: Infinity
    });

    // create our own little asyncIterator that wraps the lines asyncIterator
    //   so we can reject when we need to
    function asyncIterator() {
        const linesIterator = lines[Symbol.asyncIterator]();
        return {
            next: function() {
                if (latchedError) {
                    return Promise.reject(latchedError);
                } else {
                    return new Promise((resolve, reject) => {
                        // save reject handlers in higher scope so they can be called 
                        // from the stream error handler
                        kill.add(reject);

                        let p = linesIterator.next();

                        // have our higher level promise track the iterator promise
                        // except when we reject it from the outside upon stream error
                        p.then((data => {
                            // since we're resolving now, let's removing our reject
                            // handler from the kill storage.  This will allow this scope
                            // to be properly garbage collected
                            kill.delete(reject);
                            resolve(data);
                        }), reject);
                    });
                }
            }
        }
    }

    var asyncIterable = {
        [Symbol.asyncIterator]: asyncIterator
    };

    return asyncIterable;
}

async function runIt() {
    for await (let line of processLineByLine("xfile1.txt")) {
         console.log(line);
     }
 }

runIt().then(() => {
    console.log("done");
}).catch(err => {
    console.log("final Error", err);
});

Some explanation on how this works…

Our own error monitoring on the stream

First, you can see this:

    fileStream.on('error', (err) => {
        latchedError = err;
        // any open promises waiting on this stream, need to get rejected now
        for (let fn of kill) {
            fn(err);
        }
    });

This is our own error monitoring on the readStream to make up for the missing error handling inside of readline. Anytime we see an error, we save it in a higher scoped variable for potential later use and, if there are any pending promises registered from readline for this stream, we «kill» them (which rejects them, you will see later how that works).

No special handling for file open errors

Part of the goal here was to get rid of the special handling in the previous solution for file open errors. We want ANY error on the readStream to trigger a rejection of the asyncIterable so this is a much more general purpose mechanism. the file open error gets caught in this error handling just the same way any other read error would.

Our own asyncIterable and asyncIterator

Calling readline.createInterace() returns an asyncIterable. It’s basically the same as a regular iterable in that you call a special property on it to get an asyncIterator. That asyncIterator has a .next() property on it just like a regular iterator except when asyncIterator.next() is called, it returns a promise that resolves to an object instead of an object.

So, that’s how for await (let line of lines) works. It first calls lines[Symbol.asyncIterator]() to get an asyncIterator. Then, on that asyncIterator that it gets back, it repeatedly does await asyncIterator.next() waiting on the promise that asyncIterator.next() returns.

Now, readline.createInterface() already returns such an asyncIterable. But, it doesn’t work quite right. When the readStream gets an error, it doesn’t reject the promise returned by .next() on each iteration. In fact, that promise never gets rejected or resolved. So, things get stalled. In my test app, the app would just exit because the readStream was done (after the error) and there was no longer anything keeping the app from exiting, even though a promise was still pending.

So, I needed a way to force that promise that readlineIterator.next() had previously returned and was currently being awaited by for await (...) to be rejected. Well, a promise doesn’t provide an outward interface for rejecting it and we don’t have access to the internals to the readline implementation where there is access to reject it.

My solution was to wrap the readlineIterator with my own as a sort of proxy. Then, we my own error detector sees an error and there are promise(s) outstanding from readline, I can use my proxy/wrapper to force a rejection on those outstanding promise(s). This will cause the for await (...) to see the reject and get a proper error. And, it works.

It took me awhile to learn enough about how asyncIterators work to be able to wrap one. I owe a lot of thanks to this Asynchronous Iterators in JavaScript article which provided some very helpful code examples for constructing your own asyncIterable and asyncIterator. This is actually where the real learning came about in this exercise and where others might learn by understanding how this works in the above code.

Forcing a wrapped promise to reject

The «ugliness» in this code comes in forcing a promise to reject from outside the usual scope of the reject handler for that promise. This is done by storing the reject handler in a higher level scope where an error handling for the readStream can call trigger that promise to reject. There may be a more elegant way to code this, but this works.

Making our own asyncIterable

An async iterable is just an object that has one property on it named [Symbol.asyncIterator]. That property must be a function that, when called with no arguments, returns an asyncIterator. So, here’s our asyncIterable.

var asyncIterable = {
    [Symbol.asyncIterator]: asyncIterator
};

Making our own asyncIterator

An asyncIterator is a function that when called returns an object with a next() property on it. Each time obj.next() is called, it returns a promise that resolves to the usual iterator tuple object {done, value}. We don’t have to worry about the resolved value because we’ll just get that from the readline’s iterator. So, here’s our asyncIterator:

// create our own little asyncIterator that wraps the lines asyncIterator
//   so we can reject when we need to
function asyncIterator() {
    const linesIterator = lines[Symbol.asyncIterator]();
    return {
        next: function() {
            if (latchedError) {
                return Promise.reject(latchedError);
            } else {
                return new Promise((resolve, reject) => {
                    // save reject handlers in higher scope so they can be called 
                    // from the stream error handler
                    kill.push(reject);

                    let p = linesIterator.next();

                    // have our higher level promise track the iterator promise
                    // except when we reject it from the outside upon stream error
                    p.then(resolve, reject);
                });
            }
        }
    }
}

First, it gets the asyncIterator from the readline interface (the one we’re proxying/wrapping) and stores it locally in scope so we can use it later.

Then, it returns the mandatory iterator structure of the form {next: fn}. Then, inside that function is where our wrapping logic unfolds. If we’ve seen a previous latched error, then we just always return Promise.reject(latchedError);. If there’s no error, then we return a manually constructed promise.

Inside the executor function for that promise, we register our reject handling by adding it into a higher scoped Set named kill. This allows our higher scoped filestream.on('error', ....) handler to reject this promise if it sees an error by calling that function.

Then, we call linesIterator.next() to get the promise that it returns. We register an interest in both the resolve and reject callbacks for that promise. If that promise is properly resolved, we remove our reject handler from the higher level scope (to enable better garbage collection of our scope) and then resolve our wrap/proxy promise with the same resolved value.

If that linesIterator promise rejects, we just pass the reject right through our wrap/proxy promise.

Our own filestream error handling

So, now the final piece of explanation. We have this error handler watching the stream:

fileStream.on('error', (err) => {
    latchedError = err;
    // any open promises waiting on this stream, need to get rejected now
    for (let fn of kill) {
        fn(err);
    }
});

This does two things. First, it stores/latches the error so any future calls to the lines iterator will just reject with this previous error. Second, if there are any pending promises from the lines iterator waiting to be resolved, it cycles through the kill Set and rejects those promises. This is what gets the asyncIterator promise to get properly rejected. This should be happening inside the readline code, but since it isn’t doing it properly, we force our wrap/proxy promise to reject so the caller sees the proper rejection when the stream gets an error.


In the end, you can just do this as all the ugly detail is hidden behind the wrapped asyncIterable:

async function runIt() {
    for await (let line of processLineByLine("xfile1.txt")) {
         console.log(line);
     }
 }

runIt().then(() => {
    console.log("done");
}).catch(err => {
    console.log("final Error", err);
});

Apex Legends – компьютерная игра, которая в буквальном смысле взорвала мировые игровые чарты. Однако, к сожалению, данный тайтл жанра Батл Рояль не без изъянов: низкий FPS, тормоза, вылеты, краши, зависания и прочие баги. Все чаще геймеры сталкиваются с проблемами еще в самом начале, например, при запуске Апекс появляется ошибка такого содержания:

Apex Legends - ошибка CSOM MilesAsync OpenOrFindFile

Пользователи нашли решения быстрого ее устранения.

Перезапуск Origin

Чтобы при запуске Apex Legends избавиться от ошибки CSOM MilesAsync OpenOrFindFile вам нужно переустановить клиент Ориджин. Ваши действия:

  • Запустите Origin и откройте вкладку «Библиотека».
  • Здесь выберете свою игру Apex Legends.

игра

  • На экране видеоигры непосредственно под кнопкой «Играть» нажмите на шестеренку настроек.

кнопка

  • Выберите в контекстном меню действие «Восстановить».

восстановить

  • После этого запустится процесс проверки актуальности файлов. При обнаружении недостающих они загрузятся автоматически.

Когда процесс восстановления закончится, перезапустите Origin. Теперь запускайте Apex Legends – проблема с указанной ошибкой должна быть успешно решена. Однако если не помогло, то проделайте следующую процедуру.

Переустановка Origin

В случае, когда первый вариант не подошел вам и ошибка «CSOM MilesAsync OpenOrFindFile…» все еще выскакивает, попробуйте полностью переустановить клиент Ориджин. Достаточно часто источник проблемы с настройками видеоигры Apex Legends или конфигурацией ПК совершенно не связаны. В данном случае причиной неполадки является неправильная установка Origin.
Это можно быстро исправить, переустановив клиент, причем без потребности повторного скачивания самой игры. Достаточно приложение удалить, затем переустановить, указав папку, где собственно он ранее был установлен. Итак:

  • Нажмите комбинацию клавиш Win+R.
  • Затем в диалоговом окне вам нужно набрать команду appwiz.cpl.
  • Найдите в перечне Origin и кликните по ней. Нажмите «Удалить».

удалить

  • Скачайте с официального источника последнюю версию:

    https://origin-pro.ru/

    и запустите установочный процесс.

Помните программу необходимо установить исключительно в папку, в которой она находилась раньше. Поэтому вам нужно проверить, что при установке в окне отображается правильный путь. Таким образом, Origin установится заново и перезапишет файлы из предыдущей версии. Непосредственно Apex Legends при этом задета не будет, и загружать ее снова не потребуется. После установки запустите клиент и проверьте, удалось ли устранить проблему запуска игры. Если нет – примените еще один вариант ее решения.

Смена языка

В настройках Apex Legends есть функция смены языка. Если с запуском игры возникают сбои такие как ошибка «CSOM MilesAsync OpenOrFindFile» многие пользователи устранили ее временным изменением языка. Это заставляет установщика игры загрузить функциональные файлы, которые может быть были повреждены.

  • Откройте Ориджин.
  • В библиотеке кликните правой кнопкой мыши на Апекс.
  • Выберите «Свойства игры».

свойства

  • В параметрах пройдите в раздел «Расширенные настройки запуска» и смените русский язык на «English US». Нажмите «Сохранить».

сохранить

  • Затем снова ПКМ на Apex Legends нажмите «Восстановить».

восстановить

После проделанных манипуляций попробуйте запустить игру, если все прошло успешно, то переключите язык снова на русский.

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Fs 1060dn сбросить ошибку
  • Fs 1035 ошибка 1101
  • Frosty mod manager there was an error when trying to load game using specified profile
  • Frosty editor there was an error when trying to load game using specified profile
  • Frostpunk ошибка 0xc000007b

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии