在 forEach 循环中使用 Promise
Using a Promise inside of a forEach loop
我是 Promise
的新手,不太了解这一点。
我正在尝试创建一种情况,如果页面上的某个元素需要它,我会加载 google 地图 API 脚本。这部分我已经开始工作了,我正在努力解决的问题是,如果页面上有超过 1 个元素需要 google 映射 API 我只需要加载一次脚本。
这是我目前的情况。
index.html
<div class="map" id="map-1" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-2" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-3" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-4" data-module="map" style="height: 100vh;"></div>
loadGoogleMapsApi.js
export default class LoadGoogleMapsAPI {
constructor() {
this.apiKey = '********';
// set a globally scoped callback if it doesn't already exist
/* eslint no-underscore-dangle: 0 */
if (!window._GoogleMapsApi) {
this.callbackName = '_GoogleMapsApi.mapLoaded';
window._GoogleMapsApi = this;
window._GoogleMapsApi.mapLoaded = this.mapLoaded.bind(this);
}
}
/**
* Load the Google Maps API javascript
*/
async load() {
if (!this.promise) {
this.promise = await new Promise((resolve) => {
this.resolve = resolve;
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.src = `//maps.googleapis.com/maps/api/js?key=${window._GoogleMapsApi.apiKey}&callback=${window._GoogleMapsApi.callbackName}`;
script.async = true;
document.body.append(script);
} else {
this.resolve();
}
});
}
return this.promise;
}
/**
* Globally scoped callback for the map loaded
*/
mapLoaded() {
if (this.resolve) {
this.resolve();
}
}
}
map.js
import GoogleMapsApi from '../utils/loadGoogleMapsApi';
export default class MapViewModel {
constructor(module) {
this.module = module;
const gmapApi = new GoogleMapsApi();
gmapApi.load().then(() => {
// safe to start using the API now
new google.maps.Map(this.module, {
center: { lat: 51.5074, lng: -0.1278 },
zoom: 11,
});
// etc.
});
}
static init() {
const instances = document.querySelectorAll('[data-module="map"]');
instances.forEach((module) => {
const options = JSON.parse(module.getAttribute('data-map-settings'));
new MapViewModel(module, options);
});
}
}
MapViewModel.init();
问题出在 load()
函数中(我认为)。我尝试了各种不同的东西,这是我最接近的。似乎代码要么不等待并将脚本标记放入 4 次,要么代码在脚本标记加载之前解析并且我的 google.maps.Map(...)
不起作用。
我将不胜感激任何帮助。
干杯,
卢克.
更新
已解决
感谢@jcubic 帮助我最终找到解决方案的新代码。
loadGoogleMapsApi.js
export default class LoadGoogleMapsAPI {
/**
* Load the Google Maps API javascript
*/
static load() {
this.apiKey = '******';
if (!this.promise) {
this.promise = new Promise((resolve) => {
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.onload = resolve;
script.src = `//maps.googleapis.com/maps/api/js?key=${this.apiKey}`;
script.async = true;
document.body.append(script);
}
});
}
return this.promise;
}
}
map.js
import GoogleMapsApi from '../utils/loadGoogleMapsApi';
export default class MapViewModel {
constructor(module) {
this.module = module;
GoogleMapsApi.load().then(() => {
// safe to start using the API now
new google.maps.Map(this.module, {
center: { lat: 51.5074, lng: -0.1278 },
zoom: 11,
});
// etc.
});
}
static init() {
const instances = document.querySelectorAll('[data-module="map"]');
instances.forEach((module) => {
const options = JSON.parse(module.getAttribute('data-map-settings'));
new MapViewModel(module, options);
});
}
}
MapViewModel.init();
所以解决方案的两个部分是使 loadGoogleMapsApi.js 成为静态 class 并将 constructor
代码移动到 load()
函数中。然后还更改 load()
函数以不使用 async/await 并添加 script.onload = resolve
.
如果你使用这个 this.promise = await new Promise((resolve) => {
this.promise 将不是一个承诺而是承诺解决的价值,这就是 async/await 的工作方式。你用未定义的方式解析它(解析()没有价值)所以 this.promise
是未定义的(它总是错误的)。
EDIT 你还需要调用 this.resolve 否则如果你在循环中调用你在它完成之前多次执行它,你可能还想解决承诺脚本准备就绪时间:
load() {
if (!this.promise) {
this.promise = new Promise((resolve) => {
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.onload = resolve;
script.src = `//maps.googleapis.com/maps/api/js?key=${window._GoogleMapsApi.apiKey}&callback=${window._GoogleMapsApi.callbackName}`;
script.async = true;
document.body.append(script);
}
});
}
return this.promise;
}
我是 Promise
的新手,不太了解这一点。
我正在尝试创建一种情况,如果页面上的某个元素需要它,我会加载 google 地图 API 脚本。这部分我已经开始工作了,我正在努力解决的问题是,如果页面上有超过 1 个元素需要 google 映射 API 我只需要加载一次脚本。
这是我目前的情况。
index.html
<div class="map" id="map-1" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-2" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-3" data-module="map" style="height: 100vh;"></div>
<div class="map" id="map-4" data-module="map" style="height: 100vh;"></div>
loadGoogleMapsApi.js
export default class LoadGoogleMapsAPI {
constructor() {
this.apiKey = '********';
// set a globally scoped callback if it doesn't already exist
/* eslint no-underscore-dangle: 0 */
if (!window._GoogleMapsApi) {
this.callbackName = '_GoogleMapsApi.mapLoaded';
window._GoogleMapsApi = this;
window._GoogleMapsApi.mapLoaded = this.mapLoaded.bind(this);
}
}
/**
* Load the Google Maps API javascript
*/
async load() {
if (!this.promise) {
this.promise = await new Promise((resolve) => {
this.resolve = resolve;
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.src = `//maps.googleapis.com/maps/api/js?key=${window._GoogleMapsApi.apiKey}&callback=${window._GoogleMapsApi.callbackName}`;
script.async = true;
document.body.append(script);
} else {
this.resolve();
}
});
}
return this.promise;
}
/**
* Globally scoped callback for the map loaded
*/
mapLoaded() {
if (this.resolve) {
this.resolve();
}
}
}
map.js
import GoogleMapsApi from '../utils/loadGoogleMapsApi';
export default class MapViewModel {
constructor(module) {
this.module = module;
const gmapApi = new GoogleMapsApi();
gmapApi.load().then(() => {
// safe to start using the API now
new google.maps.Map(this.module, {
center: { lat: 51.5074, lng: -0.1278 },
zoom: 11,
});
// etc.
});
}
static init() {
const instances = document.querySelectorAll('[data-module="map"]');
instances.forEach((module) => {
const options = JSON.parse(module.getAttribute('data-map-settings'));
new MapViewModel(module, options);
});
}
}
MapViewModel.init();
问题出在 load()
函数中(我认为)。我尝试了各种不同的东西,这是我最接近的。似乎代码要么不等待并将脚本标记放入 4 次,要么代码在脚本标记加载之前解析并且我的 google.maps.Map(...)
不起作用。
我将不胜感激任何帮助。
干杯, 卢克.
更新
已解决
感谢@jcubic 帮助我最终找到解决方案的新代码。
loadGoogleMapsApi.js
export default class LoadGoogleMapsAPI {
/**
* Load the Google Maps API javascript
*/
static load() {
this.apiKey = '******';
if (!this.promise) {
this.promise = new Promise((resolve) => {
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.onload = resolve;
script.src = `//maps.googleapis.com/maps/api/js?key=${this.apiKey}`;
script.async = true;
document.body.append(script);
}
});
}
return this.promise;
}
}
map.js
import GoogleMapsApi from '../utils/loadGoogleMapsApi';
export default class MapViewModel {
constructor(module) {
this.module = module;
GoogleMapsApi.load().then(() => {
// safe to start using the API now
new google.maps.Map(this.module, {
center: { lat: 51.5074, lng: -0.1278 },
zoom: 11,
});
// etc.
});
}
static init() {
const instances = document.querySelectorAll('[data-module="map"]');
instances.forEach((module) => {
const options = JSON.parse(module.getAttribute('data-map-settings'));
new MapViewModel(module, options);
});
}
}
MapViewModel.init();
所以解决方案的两个部分是使 loadGoogleMapsApi.js 成为静态 class 并将 constructor
代码移动到 load()
函数中。然后还更改 load()
函数以不使用 async/await 并添加 script.onload = resolve
.
如果你使用这个 this.promise = await new Promise((resolve) => {
this.promise 将不是一个承诺而是承诺解决的价值,这就是 async/await 的工作方式。你用未定义的方式解析它(解析()没有价值)所以 this.promise
是未定义的(它总是错误的)。
EDIT 你还需要调用 this.resolve 否则如果你在循环中调用你在它完成之前多次执行它,你可能还想解决承诺脚本准备就绪时间:
load() {
if (!this.promise) {
this.promise = new Promise((resolve) => {
if (typeof window.google === 'undefined') {
const script = document.createElement('script');
script.onload = resolve;
script.src = `//maps.googleapis.com/maps/api/js?key=${window._GoogleMapsApi.apiKey}&callback=${window._GoogleMapsApi.callbackName}`;
script.async = true;
document.body.append(script);
}
});
}
return this.promise;
}