[Resolve] deadline.timeRemaining is not a function.

An error has occurred in iOS Safari.

Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1
TypeError: deadline.timeRemaining is not a function.

Below is the related code.

let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5)

Below is the entire class that contains the above code.(work(deadline) {...})

const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
        constructor(view) {
            this.view = view;
            this.working = null;
            this.workScheduled = 0;
            // End of the current time chunk
            this.chunkEnd = -1;
            // Milliseconds of budget left for this chunk
            this.chunkBudget = -1;
            this.work = this.work.bind(this);
            this.scheduleWork();
        }
        update(update) {
            let cx = this.view.state.field(Language.state).context;
            if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
                this.scheduleWork();
            if (update.docChanged) {
                if (this.view.hasFocus)
                    this.chunkBudget += 50 /* ChangeBonus */;
                this.scheduleWork();
            }
            this.checkAsyncSchedule(cx);
        }
        scheduleWork() {
            if (this.working)
                return;
            let { state } = this.view, field = state.field(Language.state);
            if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
                this.working = requestIdle(this.work);
        }
        work(deadline) {
            this.working = null;
            let now = Date.now();
            if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
                this.chunkEnd = now + 30000 /* ChunkTime */;
                this.chunkBudget = 3000 /* ChunkBudget */;
            }
            if (this.chunkBudget <= 0)
                return; // No more budget
            let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
            if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
                return;
            let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
            let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
            let done = field.context.work(() => {
                return isInputPending && isInputPending() || Date.now() > endTime;
            }, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
            this.chunkBudget -= Date.now() - now;
            if (done || this.chunkBudget <= 0) {
                field.context.takeTree();
                this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
            }
            if (this.chunkBudget > 0 && !(done && !viewportFirst))
                this.scheduleWork();
            this.checkAsyncSchedule(field.context);
        }
        checkAsyncSchedule(cx) {
            if (cx.scheduleOn) {
                this.workScheduled++;
                cx.scheduleOn
                    .then(() => this.scheduleWork())
                    .catch(err => logException(this.view.state, err))
                    .then(() => this.workScheduled--);
                cx.scheduleOn = null;
            }
        }
        destroy() {
            if (this.working)
                this.working();
        }
        isWorking() {
            return !!(this.working || this.workScheduled > 0);
        }
    }, {
        eventHandlers: { focus() { this.scheduleWork(); } }
    });

When I checked the console, the argument “deadline” was a number, not an object.

I’m not sure if it’s correct, but with the following changes, it works as expected.

let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, ("number" === typeof deadline ? deadline : deadline.timeRemaining() - 5)) : 1e9);

This seems to suggest that browser has a requestIdleCallback implementation that doesn’t conform to the standard at all, which is odd — MDN claims Safari simply doesn’t provide this function. Are you loading any kind of polyfill for it? Does the problem also occur in the editor on CodeMirror 6?

Excuse me. I was really misunderstanding.
This seems to have been caused by the polyfill I had implemented on my own.
I set polyfill as below.

if(!("requestIdleCallback" in self)) {
    self["requestIdleCallback"] = requestAnimationFrame;
  }

I apologize for the inconvenience caused by the fact that it was implemented by myself.

Ah, right, that explains it. Glad you figured it out.

1 Like