I observe that In angular 2 there is no finally block for promise API
angular 1 :
loadUsers() {
fetch('/api/users').then((response) => {
return response.json();
}).then((data) => {
this.users = data;
}).catch((ex) => {
console.error('Error fetching users', ex);
}).finally(() => {
this.userLoaded = true;
};
Assuming I have to do same thing in angular 2
How to add finally block in angular 2 promise , as of now there are only then & catch blocks available in angular 2. If not finally then is there any way to add cleanup code after execution of each method , where do i write code to do finally block activities ?
The easiest way to do this is to use the promise.finally shim.
Add it with npm install --save promise.prototype.finally
Add the typings: npm install --save-dev #types/promise.prototype.finally
In your main class, before bootstrapping the application, add the following code:
import { shim } from 'promise.prototype.finally';
shim();
You should now be able to use finally on your promises.
This is usually done using Promise.always. This takes one function, and adds a new .then on the promise that gives the same function for both success and fail states. If the function is not available in the given promise-based environment, it's pretty easy to polyfill in.
Promise.always = function(p, fn) {
return p.then(fn, fn);
}
usage:
var prom = fetch('/api/users').then...
Promise.always(prom, () => {
this.userLoaded = true;
});
return prom;
Modern solution in 2019-2020
First of all, you should avoid manually adding polyfills without a good reason for it, don't do it blindly because you've read it somewhere.
The problem you encounter has two sides: typing declarations and the implementation.
In order to use Promise.finally in your code without typing errors you should add es2018.promise to the lib option in your tsconfig.json file.
For modern projects you should use the following configuration (which is default in Angular 8):
{
"compilerOptions": {
…
"lib": [
"es2018",
"dom"
]
}
}
This will fix the typing errors you have in the editor.
Also, according to the docs and my observations the correct polyfill for Promise.finally will be added by the Angular compiler automatically, you don't have to install or add anything.
But, in general, you could add a polyfill (only if it is proven to be required) in ./src/polyfills.ts file using core-js library. Always try to use core-js instead of other libraries, cause it's an industry standard and it's used by Angular internally.
For example, Promise.finally polyfill could be added like this:
import 'core-js/features/promise/finally';
See more at core-js documentation.
Also, make sure to read browser-support and differential-loading articles of Angular documentation.
Related
I'm searching for some eslint option, or some other way to detect missing the 'await' keyword before calling async methods inside a class. Consider the following code:
const externalService = require('./external.service');
class TestClass {
constructor() { }
async method1() {
if (!await externalService.someMethod()) {
await this.method2();
}
}
async method2() {
await externalService.someOtherMethod();
}
module.exports = TestClass;
There will be no warning if I will convert method1 to:
async method1() {
if (!await externalService.someMethod()) {
this.method2();
}
}
I tried to do on the '.eslintrc' file:
"require-await": 1,
"no-return-await": 1,
But with no luck. Anyone have an idea if it is even possible?
Thanks a lot!
typescript-eslint has a rule for this: no-floating-promises
This rule forbids usage of Promise-like values in statements without handling their errors appropriately ... Valid ways of handling a Promise-valued statement include awaiting, returning, and either calling .then() with two arguments or .catch() with one argument.
As you probably figured out from the name, typescript-eslint is designed to add TypeScript support to eslint, but you can use it with JavaScript as well. I guess it's your call to decide whether it's overkill for this one rule, but here are the steps:
Generate a tsconfig.json file
npx tsc --init
Install dependencies
npm install --save-dev eslint #typescript-eslint/eslint-plugin #typescript-eslint/parser
Modify your .eslintrc file
Based on my testing it looks like you'll need these entries at a minimum:
{
"parser": "#typescript-eslint/parser",
"parserOptions": { "project": "./tsconfig.json" },
"plugins": ["#typescript-eslint"],
"rules": {
"#typescript-eslint/no-floating-promises": ["error"]
}
}
If there are places where you want to call an async function without using await, you can either:
Use the void operator as mentioned in the documentation for the rule, e.g.
void someAsyncFunction();
Or just change error to warn in the .eslintrc configuration above
The documentation for setting up typescript-eslint is here for more info: https://typescript-eslint.io/docs/linting/linting
Next time you run eslint you should see the rule applied:
$ npm run lint
...
./services/jobService.js
11:5 warning Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator #typescript-eslint/no-floating-promises
Since you mentioned VS Code specifically, this also integrates great with the ESLint plugin:
require-await says "Don't make a function async unless you use await inside it".
This is because async has two effects:
It forces the function to return a promise
It lets you use await inside it
The former is rarely useful which mean if you aren't using await inside the function you need to question why you marked it as async.
no-return-await stops you from doing:
return await something
Because await unwraps a value from a promise, but returning a value from an async function wraps it in a promise.
Since just returning a promise causes that promise to be adopted, combining return with await is just bloat.
So neither of those do what you want.
Which brings us to your actual desire.
Such a feature does not (as far as I know) exist in ESLint, and I don't think it would be useful to have one.
There are lots of use cases where you don't want to await something returned by an async function.
e.g.
const array_of_promises = array_of_values.map( value => do_something_async(value) );
const array_of_resolved_values = await Promise.all(array_of_promises);
The above is a common use-case where you want to run a bunch of async functions in parallel and then wait for them all to resolve.
Another example is the case that no-return-await is designed to detect!
Cases like these are common enough that most people wouldn't want their toolchain calling them out for doing it.
There is an ESLint plugin for this that you can use as an alternative to the typescript-eslint route:
https://github.com/SebastienGllmt/eslint-plugin-no-floating-promise
Destructuring from props is not working inside an async function while it's working fine if I use it using this.props.
This is for a react-native app already in production which suddenly started giving this error 2 days back.
I've tried upgrading babel using this
But no success so far.
If I use this.props.getLoginData instead, it works fine
If I use following function, it's erroneous:
yo = async () => { // with async
const { getLoginData } = this.props; // error
};
While the following function works fine:
yo = () => { // without async
const { getLoginData } = this.props;
console.log(getLoginData); // works fine
};
This also works fine:
yo = async () => { // with async
console.log(this.props.getLoginData); // works fine
};
I expect both of the scenarios to run fine.
Please clone and run this repo to reproduce this bug.
Please find the steps to run the project and environment info in README.md.
P.S.: You will find the error in console( Press ⌘⌥I )
It looks like a dependency of babel is the cause of the issue in my case.
When I look in my package-lock.json, and search for plugin-transform-regenerator I see version 7.4.5. Locking the version to 7.4.4 by adding it to my package.json allows me to build without issue.
This problem would have been easier to track down if I was not ignoring my package-lock.json.
In summary,
npm i -E #babel/plugin-transform-regenerator#7.4.4
I've encountered the same problem.
Using babel version 7.4.4 didn't help me, but I've found another solution that worked - wrapping the destructure in try-catch block.
I still don't know why this problem happens though - will update when I do.
________UPDATE_______
Eventually, the solution #makenova offered worked (Thanks man!).
What I had to do is to remove all node modules + package-lock, then run
npm i -E #babel/plugin-transform-regenerator#7.4.4
and after that run
npm i
Before that I've used yarn and it didn't do the trick.
Update:
nicolo-ribaudo fixed the issue here: https://github.com/facebook/regenerator/pull/377
An alternative solution is to force the regenerator-transform to use ~0.13.0 as suggested by nicolo-ribaudo.
If you are using yarn, add this to your package.json:
"resolutions": {
"regenerator-transform": "~0.13.0"
}
If you are using npm:
Install it as a devDependency
Delete package-lock.json
Re-install dependencies
I've a async function, when destructuring and save const show me error: Possible Unhandled Promise Rejection (id: 0): Error: “userOperations” is read-only , this worked for me (change let by const):
https://github.com/facebook/regenerator/issues/375#issuecomment-527209159
I tried to load Cycle DOM from their CDN through SystemJS with something like:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
}
});
System.import('cycle-dom', cycleDOM => {
...
});
But I quickly found out cycle-dom needs xstream. So I try to load both:
Promise.all([
System.import('xstream'),
System.import('cycle-dom')
])
.then(([xs, cycleDOM]) => {
...
});
But I still get the same error. It looks like cycle-dom is expecting xstream to exist on window when it's first loaded. So I tried:
System.import('xstream')
.then(xs => window['xstream'] = xs)
.then(() => System.import('cycle-dom'))
.then(cycleDOM => {
...
});
I feel like I'm going about this all wrong. How can I do this?
Update:
Following martin's advice below, I tried configuring xstream as a dependency of cycle-dom.
Here's a jsbin that demonstrates. What I'm doing is loading cycle-run and cycle-dom and then running the example off the cycle home page.
But I get the error:
"TypeError: Cannot read property 'default' of undefined"
Undefined in this case is cycle-dom trying to load window['xstream'], which isn't being loaded.
Thanks.
The System.import() call returns a Promise so you need to put the callback into its then() method (the second parameter is the parent name; not a callback).
System.import('cycle-dom').then(function(cycleDOM) {
console.log(cycleDOM);
});
This prints the module exports.
I don't have any experience with cycle.js so I can't tell whether this is enough or not. Nonetheless you can set this package dependencies with meta config:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
},
meta: {
'cycle-dom': {
deps: [
'xstream'
]
}
}
});
Again, I don't know whether this is enough or not. The SystemJS documentation contains pretty well explained example how to load dependencies that need to register some global variables. See https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#shim-dependencies
Edit:
In this case it's a little more complicated. The cycle-run.js script is generated probably by browserify and you can see it contains a line as follows:
var xstream_1 = (typeof window !== "undefined" ? window['xstream'] : typeof global !== "undefined" ? global['xstream'] : null);
This checks whether window['xstream'] exists when it's loaded. This means that the xstream has to be loaded before loading the cycle-run.js script. The way SystemJS works is that it loads the requested module and then loads its dependencies (you can see the order in Developer Tools). So it's the opposite order than you need (this is very similar to my question on SystemJS GitHub page).
This means you need to restructure the import calls:
System.config({
// ...
meta: {
'xstream': {
format: 'global',
exports: 'xstream',
}
}
});
System.import('xstream').then(function() {
Promise.all([
System.import('cycle-run'),
System.import('cycle-dom'),
])
.then(([cycle, cycleDOM]) => {
// ...
});
});
This registers the xstream before loading cycle-run. Also with the meta configuration for xstream this ensures that the window.xstream exists only inside these callbacks and doesn't leak to the global scope.
See your updated demo: https://jsbin.com/qadezus/35/edit?js,output
Also to use format and exports you need to use the newer SystemJS 0.20.* and not 0.19.*.
I'm attempting to test a promise with Jest CLI, this code executes as it should when it's run in the browser. However I want to start writing tests for it.
class ListCollection {
constructor() {
this.items = new Array();
}
addItem(string) {
const addItemPromise = new Promise(
function (resolve, reject) {
// set up async getting like a XMLHttpRequest
setTimeout( () => {
this.items.push(string);
resolve(string);
}.bind(this), 2000);
}.bind(this)
);
return addItemPromise;
}
}
Currently I'm trying to get this very basic test to work. I'm testing with pit as per the documentation which links to jasmine-pit.
jest.dontMock('../collections');
import Collection from '../collections';
describe("Collection", () => {
let collection;
beforeEach(() => {
collection = new Collection();
});
describe("Adding an item", () => {
pit('Spec 1', function () {
return collection.addItem("Hello World").then(function (string) {
expect(string).toBe("Hello World");
});
});
});
})
When I run my tests with babel-node ./node_modules/.bin/jest, the test above fails with this as it's stack trace. Notably I get Promise is not defined.
Rollos-Mac-Pro:react-boilerplate Rollo$ babel-node ./node_modules/.bin/jest
Using Jest CLI v0.4.0
FAIL app/collections/__tests__/collectionTests.js
ReferenceError: /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js: /Users/Rollo/react-boilerplate/app/collections/collections.js: **Promise is not defined**
at ListCollection.addItem (/Users/Rollo/react-boilerplate/app/collections/collections.js:24:34)
at /Users/Rollo/react-boilerplate/app/collections/collections.js:48:12
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at Loader.requireModuleOrMock (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:937:17)
at /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js:7:34
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at jasmineTestRunner (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/jasmineTestRunner/jasmineTestRunner.js:242:16)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/TestRunner.js:371:12
at _fulfilled (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:798:54)
at self.promiseDispatch.done (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:827:30)
at Promise.promise.promiseDispatch (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:760:13)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:574:44
at flush (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:108:17)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/FakeTimers.js:325:7
at process._tickCallback (node.js:448:13)
I don't know how to fix this. The node version I'm using has to be 0.10.x, otherwise I can't run the Jest-CLI. But node version 0.10.x doesn't have promises. I also don't understand how Jest-CLI works with my ES6 classes and syntax but won't recognize Promises.
Any idea how to get promises to work in my setup?
EDIT
I've added the es6-promise polyfill to the top of my test file and flagged it not to be mocked. This provides an adequate fix.
jest.dontMock('es6-promise');
require('es6-promise').polyfill();
The output mentions babel-node, which means that babel is running as a require hook, compiling your ES6 to ES5 on the fly. You need to include a shim for native promises, and expose it as global.Promise. I'd recommend this one: https://github.com/jakearchibald/es6-promise
replace jest scriptPreprocessor with babel-jest should work fine for all es6 comparability issues
https://babeljs.io/docs/using-babel/#jest
I am currently using requirejs to manage module js/css dependencies.
I'd like to discover the possibilities of having node do this via a centralized config file.
So instead of manually doing something like
define([
'jquery'
'lib/somelib'
'views/someview']
within each module.
I'd have node inject the dependencies ie
require('moduleA').setDeps('jquery','lib/somelib','views/someview')
Anyway, I'm interested in any projects looking at dependency injection for node.
thanks
I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.
So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.
It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.
I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.
Module code
For example, suppose you have following statements in code.js:
fs = require('fs');
console.log(fs.readFileSync('text.txt', 'utf-8'));
If you run this code with node code.js, then it will print out the contents of text.txt.
Injector code
However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:
var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');
function dependencyLookup (file) {
switch (file) {
case 'fs': return { readFileSync: function () { return "test contents"; } };
default: return origRequire(file);
}
}
If you now run node test.js, it will print out "test contents", even though it includes code.js.
I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.