我的应用程序中有一个i18n服务,其中包含以下代码:
var i18nService = function() {
this.ensureLocaleIsLoaded = function() {
if( !this.existingPromise ) {
this.existingPromise = $q.defer();
var deferred = this.existingPromise;
var userLanguage = $( "body" ).data( "language" );
this.userLanguage = userLanguage;
console.log( "Loading locale '" + userLanguage + "' from server..." );
$http( { method:"get", url:"/i18n/" + userLanguage, cache:true } ).success( function( translations ) {
$rootScope.i18n = translations;
deferred.resolve( $rootScope.i18n );
} );
}
if( $rootScope.i18n ) {
this.existingPromise.resolve( $rootScope.i18n );
}
return this.existingPromise.promise;
};
这个想法是用户会调用ensureLocaleIsLoaded
并等待承诺得到解决。但鉴于该函数的目的是仅确保加载了语言环境,用户可以多次调用它。
我目前只是存储一个promise,如果用户在从服务器成功检索到语言环境后再次调用该函数,则解析它。
据我所知,这是按预期工作的,但我想知道这是否是一种正确的方法。
答案 0 :(得分:86)
据我了解目前的承诺,这应该100%罚款。唯一需要理解的是,一旦解决(或拒绝),也就是对于一个推迟的对象 - 它就完成了。
如果您再次致电then(...)
,我们应立即获得(首先)解决/拒绝的结果。
对resolve()
的其他来电不会(不应该?)产生任何影响。不确定如果您尝试reject
之前resolved
(我怀疑什么都没有)的延迟对象会发生什么。
答案 1 :(得分:0)
前段时间我遇到过同样的事情,确实一个诺言只能解决一次,而其他尝试将无济于事(无错误,无警告,无then
调用)。
我决定像这样解决它:
getUsers(users => showThem(users));
getUsers(callback){
callback(getCachedUsers())
api.getUsers().then(users => callback(users))
}
只需将您的函数作为回调传递,并根据需要多次调用它即可!希望有道理。
答案 2 :(得分:0)
您应该做的是在主ng出口上放置ng-if并显示一个加载微调框。加载区域设置后,您将显示插座,并渲染组件层次结构。这样,您的所有应用程序都可以假定已加载语言环境,而无需进行检查。
答案 3 :(得分:0)
如果您需要更改promise的返回值,只需在then
中返回新值,然后在其上链接下一个then
/ catch
var p1 = new Promise((resolve, reject) => { resolve(1) });
var p2 = p1.then(v => {
console.log("First then, value is", v);
return 2;
});
p2.then(v => {
console.log("Second then, value is", v);
});
答案 4 :(得分:0)
没有明确的方法可以多次解决承诺,因为既然解决了,那就完成了。这里更好的方法是使用观察者可观察的模式,例如我编写了以下观察套接字客户端事件的代码。您可以扩展此代码以满足您的需求
const evokeObjectMethodWithArgs = (methodName, args) => (src) => src[methodName].apply(null, args);
const hasMethodName = (name) => (target = {}) => typeof target[name] === 'function';
const Observable = function (fn) {
const subscribers = [];
this.subscribe = subscribers.push.bind(subscribers);
const observer = {
next: (...args) => subscribers.filter(hasMethodName('next')).forEach(evokeObjectMethodWithArgs('next', args))
};
setTimeout(() => {
try {
fn(observer);
} catch (e) {
subscribers.filter(hasMethodName('error')).forEach(evokeObjectMethodWithArgs('error', e));
}
});
};
const fromEvent = (target, eventName) => new Observable((obs) => target.on(eventName, obs.next));
fromEvent(client, 'document:save').subscribe({
async next(document, docName) {
await writeFilePromise(resolve(dataDir, `${docName}`), document);
client.emit('document:save', document);
}
});
答案 5 :(得分:0)
您可以编写测试以确认行为。
通过运行以下测试,您可以得出结论
resolve()/ reject()调用永远不会抛出错误。
解决(拒绝)后,将保留解析值(拒绝错误) 不管随后的resolve()还是reject()调用。
您也可以查看my blog post了解详情。
/* eslint-disable prefer-promise-reject-errors */
const flipPromise = require('flip-promise').default
describe('promise', () => {
test('error catch with resolve', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise(resolve => {
try {
resolve()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
throw new Error('error thrown out side')
} catch (e) {
rs('error caught in expected location')
}
}))
test('error catch with reject', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise((_resolve, reject) => {
try {
reject()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
} catch (e) {
try {
throw new Error('error thrown out side')
} catch (e){
rs('error caught in expected location')
}
}
}))
test('await multiple times resolved promise', async () => {
const pr = Promise.resolve(1)
expect(await pr).toBe(1)
expect(await pr).toBe(1)
})
test('await multiple times rejected promise', async () => {
const pr = Promise.reject(1)
expect(await flipPromise(pr)).toBe(1)
expect(await flipPromise(pr)).toBe(1)
})
test('resolve multiple times', async () => {
const pr = new Promise(resolve => {
resolve(1)
resolve(2)
resolve(3)
})
expect(await pr).toBe(1)
})
test('resolve then reject', async () => {
const pr = new Promise((resolve, reject) => {
resolve(1)
resolve(2)
resolve(3)
reject(4)
})
expect(await pr).toBe(1)
})
test('reject multiple times', async () => {
const pr = new Promise((_resolve, reject) => {
reject(1)
reject(2)
reject(3)
})
expect(await flipPromise(pr)).toBe(1)
})
test('reject then resolve', async () => {
const pr = new Promise((resolve, reject) => {
reject(1)
reject(2)
reject(3)
resolve(4)
})
expect(await flipPromise(pr)).toBe(1)
})
test('constructor is not async', async () => {
let val
let val1
const pr = new Promise(resolve => {
val = 1
setTimeout(() => {
resolve()
val1 = 2
})
})
expect(val).toBe(1)
expect(val1).toBeUndefined()
await pr
expect(val).toBe(1)
expect(val1).toBe(2)
})
})
答案 6 :(得分:0)
不。多次解决/拒绝承诺是不安全的。从根本上说,它是一个错误,很难捕获,因为它不一定总是可复制的。
有些模式可用于在调试时跟踪此类问题。关于该主题的精彩演讲: Ruben Bridgewater —错误处理:做对了!(与问题相关的部分大约需要40分钟)
答案 7 :(得分:-2)
see github gist: reuse_promise.js
/*
reuse a promise for multiple resolve()s since promises only resolve once and then never again
*/
import React, { useEffect, useState } from 'react'
export default () => {
const [somePromise, setSomePromise] = useState(promiseCreator())
useEffect(() => {
somePromise.then(data => {
// do things here
setSomePromise(promiseCreator())
})
}, [somePromise])
}
const promiseCreator = () => {
return new Promise((resolve, reject) => {
// do things
resolve(/*data*/)
})
}