Attempting to grasp the concept of Thennables within the VSCode API. Can these TypeScript code examples be considered equivalent?

I'm looking to perform a series of modifications on a document using the VSCode API. The key function in this process is Workspace.applyEdit, which gives back a Thennable. This is my first encounter with it, and the one returned from this function doesn't behave as I anticipate.

Example 1:

import { window, workspace, WorkspaceEdit, Position } from 'vscode';

// does not work as expected, only inserts once despite logging multiple times
export function applyEditReprex() {
    let text = "\ntest\n";
    let target = window.activeTextEditor.document.uri;
    let positions = [
        new Position(10, 1),
        new Position(15, 1),
        new Position(20, 1)
    ];
    positions.reduce((applyThennable, position) => {
        return (
            applyThennable.then(() => {
                console.info("Making new edit");
                let edit = new WorkspaceEdit();
                edit.insert(target, position, text);
                workspace.applyEdit(edit);
            }))
    },
        Promise.resolve()
    ).then(() => {
        console.info("Finished edits.");
    })
}

Only one occurrence of "test" is inserted into the target document at line 12. The log shows:

Making new edit
Making new edit
Making new edit
Finished edits.

Example 2:

My attempt to rewrite the above code as chained calls:

import { window, workspace, WorkspaceEdit, Position } from 'vscode';

export function applyEditReprex2() {
    let text = "\ntest\n";
    let target = window.activeTextEditor.document.uri;
    let positions = [
        new Position(10, 1),
        new Position(15, 1),
        new Position(20, 1)
    ];
    console.info("Making new edit");
    let edit = new WorkspaceEdit();
    edit.insert(target, positions[0], text);
    workspace.applyEdit(edit).then(() => {
        console.info("Making new edit");
        let edit = new WorkspaceEdit();
        edit.insert(target, positions[1], text);
        workspace.applyEdit(edit).then(() => {
            console.info("Making new edit");
            let edit = new WorkspaceEdit();
            edit.insert(target, positions[2], text);
            workspace.applyEdit(edit).then(() => {
                console.info("Finished edits.");
            })
        })
    })
}

Now, 3 instances of "test" are added to the target file, on lines 12, 17, 22.

The log shows:

Making new edit
Making new edit
Making new edit
Finished edits.

Query

Could there be any nuances of reduce or fat arrow functions that might be causing the difference in behavior between the first snippet and the rewritten version? Alternatively, could the rewritten version not be equivalent to the reduce method in a significant way?

Answer №1

It appears that you missed returning the thenable object from the promise's .then() callback, which is crucial for promise chaining. Refer to this resource for more information:

positions.reduce((prevPromise, position) => {
    return prevPromise.then(() => {
        console.info("Making new edit");
        const edit = new WorkspaceEdit();
        edit.insert(target, position, text);
        const applyThenable = workspace.applyEdit(edit);
        return applyThenable;
//      ^^^^^^^^^^^^^^^^^^^^
    });
}, Promise.resolve())

By the way, it is suggested in the API documentation provided that you should create a single WorkspaceEdit with multiple insertions like this:

const positions = [
    new Position(10, 1),
    new Position(15, 1),
    new Position(20, 1)
];
const edit = new WorkspaceEdit();
for (const position in positions) {
    edit.insert(target, position, text);
}
workspace.applyEdit(edit).then(() => {
    console.info("Finished multi-edit.");
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The chai property "matchSnapshot" is not valid

https://i.sstatic.net/4wgqq.png After following the instructions provided at this link, I am now in the process of writing basic Jest tests for my React application that uses the Create-React-App starter kit. I came across a recommendation mentioned here ...

React Native: Issue with the data section in FlatList

I encountered an issue while using Flatlist to address a problem, but I ran into an error with the data property of my Flatlist. The error message is not very clear and I'm having trouble understanding it ( No overload matches this call. Overload 1 of ...

What is the best way to invoke a function using a string as its name?

In my grid configuration function, I am assigning column definitions. One key, valueGetter, requires a function to be called to fetch the column value. The issue I am encountering is that the API returns this value as a string. When I try to set it using ...

Is the oscillator value remaining stagnant in Safari?

Is there a way to utilize the web audio API alongside NexusUI JS for dial/knobs? When I use Chrome, the dial changes the oscillator frequency, but in Safari, it seems to be playing the default 440hz. Can anyone guide me on what could be the issue or what ...

What are the steps for integrating and expanding a JavaScript library using rollup, TypeScript, and Angular 2?

I am currently working on a project called angular2-google-maps-test and I am interested in integrating and expanding upon the JS library found at js-marker-clusterer npm install --save js-marker-clusterer It seems that this library is not structured as ...

Ways to display fresh information on webpage following data preservation in AngularJS

After saving some names in a form, I am attempting to display them alphabetically. Below is the method that contains all the saved data. The variable response.data should contain this data: function refreshNames() { NameService.getNames().then(func ...

Implementing a transition effect to the drawimage function

I am currently working on implementing a transition effect for an image inside a canvas element. I have provided a snippet below to demonstrate my progress so far. Can anyone guide me on how to incorporate a transition animation for the image within the c ...

Creating session variables in Joomla using checkboxes and AJAX

I'm currently working on implementing session variables in Joomla with AJAX when checkboxes are selected. Below is the code snippet from select_thumb.ajax.php file: $_SESSION['ss'] = $value; $response = $_SESSION['ss']; echo ...

Completely enlarge this inline-block CSS div scan-line

I am looking to create a unique scan line effect that gradually reveals text from left to right, mimicking the appearance of a cathode-ray burning text into a screen's phosphors. The concept involves sliding across black rows with transparent tips. Y ...

Ways to avoid scrolling on a fixed element

Here is the HTML setup I have... body .top .content The issue I am facing is that when scrolling reaches the end of the ul in the .top element, the background starts to scroll. This can be quite disorienting and makes the site slow on tablets. Even ...

Fade-in animation of a clock on an SVG image

I am trying to achieve a unique fade-in effect for an SVG image in my HTML. The challenge is to make the fade-in start from the top of the image and progress in a circular motion until it completes a full circle. An example of the effect I am aiming for is ...

The Dockerfile for Next13 is unable to locate the server.js file

When using the official Dockerfile from Next with the examples provided, I encounter an error while trying to build the app using a Dockerfile and docker-compose. b78-client-1 | node:internal/modules/cjs/loader:1078 b78-client-1 | throw err; b78-client ...

What is the best way to send a query in a jQuery AJAX call?

I am a beginner in working with AJAX requests and server programming. This project is part of my school assignment. I need to include the SID that was generated as a parameter for this request. Additionally, I am trying to pass in an object of colors, a st ...

How to Build a Custom Toolbar with Left and Right Aligned Elements using React.js and Material UI

Struggling with updating the toolbar on my website. Wanting the site name and logo on the left side, while login/sign-up buttons fixed to the right. Logo and title are in place, but can't get buttons to stay on right margin. Here's the code: func ...

Converting an array of objects to an array of JSON objects in TypeScript

My dilemma lies in the data I have uploaded under the _attachments variable: My aim is to format this data for insertion in the following structure: "_attachments": [ { "container": "string", "fileName": "string", "name": "string", "mim ...

Obtain the language of a Wordpress page using javascript

Is there a way to determine the language of a Wordpress page using Javascript? I have discovered a method to detect Spanish: if(window.location.href.indexOf("/es/") > -1) { However, if the website does not use Permalink Settings with "Post name", th ...

Move a div by dragging and dropping it into another div

Situation Within my project, there is a feature that involves adding a note to a section and then being able to move it to other sections, essentially tracking tasks. I have successfully implemented the functionality to dynamically add and drag notes with ...

Exploring ways to display a JavaScript object on click from a .json file

As I delve into the world of javascript and json, I find myself facing a challenge. I am looking to extract information (using the Get Information function) from a json file within a javascript function triggered by an event. The catch is, I want to accom ...

Symfony allows for the addition of an embedded collection prototype with an empty string

It seems like I must be overlooking something simple. I'm currently referencing this guide http://symfony.com/doc/current/cookbook/form/form_collections.html and aiming to include a link/button for adding more filters to a brand. However, the data-pr ...

The code snippets in the Vue3 documentation are quite peculiar

As I peruse the Vue 3 documentation, I notice a recurring pattern in how example code is presented for components: Vue.createApp({}) However, my experience with Vue 3 has been different. Instead of the above syntax, I simply use: <script> export d ...